Threads running in random order in C# - c#

I think the title speaks for itself. Simply I have some threads that run with a random order instead of the order I planned.
This is a sample code:
event strHandler strChanged;
delegate void strHandler(string str);
public Form1()
{
InitializeComponent();
strChanged += new strHandler(updatestr);
}
public void updatestr(string str)
{
Thread th = new Thread(new ParameterizedThreadStart(updatethr));
th.IsBackground = true;
th.Start(str);
}
object obj = new object();
private void updatethr(object str)
{
lock (obj)
{
SystemUtilities.SetControlPropertyThreadSafe(textBox1, "Text", (string)str);
Thread.Sleep(1000);
}
}
private void button1_Click(object sender, EventArgs e)
{
this.Text = write();
}
private string write()
{
string res = "";
strChanged(res);
for (int i = 0; i <= 5; i++)
{
res += i.ToString();
strChanged(res);
}
return res;
}
Note: SystemUtilities.SetControlPropertyThreadSafe(textBox1, "Text", (string)str) is a function (used to avoid cross thread exception) that set textBox1.Text to str.
When you press button1 this.Text will be set instantly to the result of write() function ("012345").
The string returned is res that is build inside write() starting from an empty string and, iteratively, appending numbers from 0 to 5.
When the string is created and for each number added to res, the event strChanged is raised calling updatestr method.
Every time that updatestr is called a thread is created and it starts calling updatethr.
Here textBox1.Text is set to str (that should be progressively "", "0" , "01", "012", "0123", "01234", "012345") and wait a second before exiting the method.
Using lock statement the threads created in updatestr should wait the end of the previous threads before modifying textBox1.Text.
Running this code I obtain sequences of values for textBox1.Text that don't match the expected sequence as if the threads don't start in order with their creation in updatestr.
Why does this happen? How can I fix that? Thanks in advance!
EDIT: If want to try this code you can replace SystemUtilities.SetControlPropertyThreadSafe(textBox1, "Text", (string)str) with System.Windows.Forms.MessageBox.Show(str)

I believe you are looking for a different threading strategy. It appears you need a single thread, to maintain order, that's different from the Form's main thread to finish an operation. By using a BlockingCollection, you can sequentially have a different thread operate on the string.
I would rewrite the code this way:
event strHandler strChanged;
delegate void strHandler(string str);
public Form1()
{
InitializeComponent();
Thread th = new Thread(new ThreadStart(updatethr));
th.IsBackground = true;
th.Start();
strChanged += new strHandler(updatestr);
}
BlockingCollection<string> bc = new BlockingCollection<string>();
public void updatestr(string str)
{
bc.Add(str);
}
private void updatethr()
{
while(true)
{
string str = bc.Take();
SystemUtilities.SetControlPropertyThreadSafe(textBox1, "Text", (string)str);
// Not sure why you need this here, other than simulating a long operation.
// Thread.Sleep(1000);
}
}
private void button1_Click(object sender, EventArgs e)
{
this.Text = write();
}
private string write()
{
string res = "";
strChanged(res);
for (int i = 0; i <= 5; i++)
{
res += i.ToString();
strChanged(res);
}
return res;
}

Related

Converting 2 txt files in 2 different texboxes from lowercase to uppercase

I am trying to convert two different .txt files from lower case to upper case and the main objective is to measure and display the execution time.
Everything goes well if the files are saved with upper cases in my predefined path and the program displays the execution time. In my GUI however, texts do not convert because of the following exception in text-boxes:
System.InvalidOperationException: Cross-thread operation not valid: Control "textBox2" accessed from a thread other than the thread it was created on.
namespace Threads
{
public partial class Form1 : Form
{ String prim= #"C:\Users\Wheelz\Desktop\Laborator09\fis1.txt";
String secund= #"C:\Users\Wheelz\Desktop\Laborator09\fis2.txt";
public Form1()'
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var read = File.ReadAllText(prim);
textBox1.Text = read;
}
private void button2_Click(object sender, EventArgs e)
{
var read = File.ReadAllText(secund);
textBox2.Text = read;
}
private void modifica1()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis1upper.txt", read);
textBox1.Text = textBox1.Text.ToUpper();
}
private void modifica2()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis2upper.txt", read);
textBox2.Text = textBox2.Text.ToUpper() ;
}
private void timp_Click(object sender, EventArgs e)
{
Thread firstThread = new Thread(new ThreadStart(modifica1));
Thread secondThread = new Thread(new ThreadStart(modifica2));
var ceas= new Stopwatch();
ceas.Start();
firstThread.Start();
secondThread.Start();
ceas.Stop();
if (ceas.ElapsedMilliseconds == 1)
{
cron.Text = ceas.ElapsedMilliseconds.ToString() + " milisecundă";
}
else
{
if ((ceas.ElapsedMilliseconds < 20))
cron.Text = ceas.ElapsedMilliseconds.ToString() + " milisecunde";
else
cron.Text = ceas.ElapsedMilliseconds.ToString() + " de milisecunde";
}
}
}
}
Yes, you can't share the form called in mainthread into a subthread.
You must use an Delegate to mainthread to update the textboxes.
READ:
Invoke(Delegate)
Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control's method from a different thread, you must use one of the control's invoke methods to marshal the call to the proper thread. This property can be used to determine if you must call an invoke method, which can be useful if you do not know what thread owns a control.
You can also work with backgroundworker or async
use "BeginInvoke" for update control value in Thread. like ...
private void modifica1()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis1upper.txt", read);
this.BeginInvoke(new MethodInvoker(() =>
{
textBox1.Text = textBox1.Text.ToUpper();
}));
}
private void modifica2()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis2upper.txt", read);
this.BeginInvoke(new MethodInvoker(() =>
{
textBox2.Text = textBox2.Text.ToUpper();
}));
}

Background worker stealing main thread

So I can't say for sure this is the issue but I'm just about positive it is. I have a recordset of IVR calls to make. I put the data for each one in a concurrent queue and start 5 background workers to start working from the queue. However, after making 2 calls, the calls stop coming until one person hangs up, then it moves on to call number 3,4,5 etc. Are the any issues with this code?
It seems like the background workers are blocking eachother from calling the same method...? Is that possible?
private ConcurrentQueue<DataTable> _ivrCallsQueue = new ConcurrentQueue<DataTable>();
private List<BackgroundWorker> _ivrCallers = new List<BackgroundWorker>();
public overrid void Process()
{
foreach(DataRow row in _tblRecordsToProcess.Rows)
{
_workingActionItem = actionItemDAL.GetActionItemFromId(Convert.ToInt32(row["FNActionItemId"].ToString()));
var workingActionItemsTable = actionItemDAL.GetActionItemParamValues(Convert.ToInt32(row["FNActionItemId"].ToString()));
ivrCallsQueue.Enqueue(workingActionItemsTable);
}
StartCalls();
while (_ivrCallers.Count != 0)
{
testurls = testurls;
}
}
private void StartCalls()
{
int maxLines = 5;
if (_ivrCallsQueue.Count < maxLines)
{
maxLines = _ivrCallsQueue.Count;
}
for (int i = 0; i < maxLines; i++)
{
DataTable workingCall = new DataTable();
_ivrCallsQueue.TryDequeue(out workingCall);
BackgroundWorker ivrCaller = new BackgroundWorker();
_ivrCallers.Add(ivrCaller);
ivrCaller.DoWork += delegate(object sender, DoWorkEventArgs e)
{
RequestIVR(workingCall, Convert.ToInt32(workingCall.Rows[2][0].ToString()));
_ivrCallers.Remove(ivrCaller);
};
ivrCaller.RunWorkerCompleted += (bw_AnalyzeResults);
ivrCaller.RunWorkerAsync();
}
}
private void bw_AnalyzeResults(object sender, RunWorkerCompletedEventArgs e)
{
DataTable workingCall = new DataTable();
if (_ivrCallsQueue.Count != 0)
{
_ivrCallsQueue.TryDequeue(out workingCall);
BackgroundWorker ivrCaller = new BackgroundWorker();
ivrCaller.DoWork += delegate(object completeSender, DoWorkEventArgs completeArgs)
{
RequestIVR(workingCall, Convert.ToInt32(workingCall.Rows[2][0].ToString()));
_ivrCallers.Remove(ivrCaller);
};
ivrCaller.RunWorkerCompleted += (bw_AnalyzeResults);
ivrCaller.RunWorkerAsync();
}
else
{
}
}
private void RequestIVR(DataTable workingTable,int fnActionItemID)
{
var urlRequest = "http://uccx_http_trigger:9080/test?strTestMode=1&strTaskID=" + fnActionItemID;
var webClient = new WebClient { UseDefaultCredentials = true, Proxy = WebRequest.DefaultWebProxy };
DecodeResponseType(GetValueFromElement("Response Code was ", webClient.DownloadString(urlRequest)));
}
This will spawn at most five threads that each attempt to pull the next item from the queue and process it. If the queue is empty the attempt will fail and the thread will simply exit:
private List<System.Threading.Thread> Threads = new List<System.Threading.Thread>();
private ConcurrentQueue<DataTable> _ivrCallsQueue = new ConcurrentQueue<DataTable>();
private void StartCalls()
{
int maxLines = Math.Min(5 , _ivrCallsQueue.Count);
for (int i = 0; i < maxLines; i++ )
{
System.Threading.Thread T = new System.Threading.Thread(delegate()
{
DataTable workingCall;
while (_ivrCallsQueue.TryDequeue(out workingCall))
{
RequestIVR(workingCall, Convert.ToInt32(workingCall.Rows[2][0].ToString()));
}
});
Threads.Add(T);
T.Start();
}
}
The threads will keep running until all the items have been processed.
It looks like bw_AnalyzeResults does pretty much the same thing that StartCalls() does. In other words, when the background worker has finished its work, you immediately enqueue the same work to happen again, recursively forever?
By the looks of it, you want bw_AnalyzeResults to analyze the results returned by calling your web service. That is not what is happening at the moment.
The code below taken from the bw_AnalyzeResults event handler is scheduling a background job and making itself handle the RunWorkerCompleted event. So, presumably the software keeps going around and around executing bw_AnalyzeResults forever until you kill the process?
private void bw_AnalyzeResults(object sender, RunWorkerCompletedEventArgs e)
{
ivrCaller.DoWork += delegate(object completeSender, DoWorkEventArgs completeArgs)
{
RequestIVR(workingCall, Convert.ToInt32(workingCall.Rows[2][0].ToString()));
_ivrCallers.Remove(ivrCaller);
};
ivrCaller.RunWorkerCompleted += (bw_AnalyzeResults);
}

Serial port data received handled too slowly

I am reading data from an arduino at a baud rate of 115200. The data comes in as a string on its own line in the format: <ID,Name/Data>.
I believe that the problem with my code is that it is not handling the incoming data fast enough and the incoming data is being forced to wait for old data to be processed.
The incoming string is split into the three separate categories (ID, Name, Data) and added to a data table called dtFromGrid which is bound to dataGridView1.
Is there any errors or suggestions for how to improve my code performance? Would a separate thread for the handling function work better than BeginInvoke?
serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string inData = serialPort1.ReadLine();
if (PauseButton.Text == "Pause" && inData.StartsWith("<"))
{
try
{
this.BeginInvoke(new SetGridDeleg(DoUpdate), new object[] {inData});
}
catch
{
}
}
}
private void DoUpdate(string inData) //inData passed in so that Serial port read only once
{
if (dtFromGrid == null)
{
dtFromGrid = new DataTable();
dtFromGrid.Columns.Add("Time", typeof(String));
dtFromGrid.Columns.Add("ID", typeof(String));
dtFromGrid.Columns.Add("Name", typeof(String));
dtFromGrid.Columns.Add("Data", typeof(String));
}
DataRow dr = dtFromGrid.NewRow();
TimeSpan ts = stopWatch.Elapsed;
dr["Time"] = String.Format("{0:00}:{1:00}:{2:00}.{3:000}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds);
dr["ID"] = inData.Split(new char[] { '<', ',' })[1];
dr["Name"] = inData.Split(new char[] { ',', '/' })[1];
dr["Data"] = inData.Split(new char[] { '/', '>' })[1];
dtFromGrid.Rows.InsertAt(dr, 0);
//Replace old data with new data if ID's are the same to showo list of only newest data per each ID
if (NewestButton.Text == "Chronological")
{
for (int i = 1; i < dataGridView1.Rows.Count; i++)
{
if (dtFromGrid.Rows[i].ItemArray[1].ToString() == dtFromGrid.Rows[0].ItemArray[1].ToString())
{
dtFromGrid.Rows[i].Delete();
break;
}
}
}
//Keep a maximum of 50 rows of data
if (dtFromGrid.Rows.Count == 51)
{
dtFromGrid.Rows[50].Delete();
}
dtFromGrid.AcceptChanges();
dataGridView1.DataSource = dtFromGrid;
//keep focus of dataGridView on top row
dataGridView1.CurrentCell = dataGridView1.Rows[0].Cells[0];
// add newest row to a logfile if the user has set one
if (logFile != "")
{
using (StreamWriter sw = File.AppendText(logFile))
{
DataRow row = dtFromGrid.Rows[0];
object[] array = row.ItemArray;
int col = 0;
for (col = 0; col < array.Length - 1; col++)
{
sw.Write(array[col].ToString() + "\t|\t");
}
sw.Write(array[col].ToString());
sw.WriteLine();
sw.Close();
}
}
}
Update
I am now using a separate thread as suggested but I am having errors with invoking inside of that thread. I get multiple errors at random but the most common is "Index out of range." My invoke code is as follows:
this.Invoke((MethodInvoker) delegate
{
dtFromGrid.AcceptChanges();
dataGridView1.DataSource = dtFromGrid;
dataGridView1.CurrentCell = dataGridView1.Rows[0].Cells[0];
});
You store the data in a queue and offload the work to a secondary thread. This only works if, on the average, you are able to process the data at the rate it is coming in. Otherwise, the size of the queue will keep growing as you fall behind.
First, start with a wrapper around Queue<T> that will allow one thread to write to the queue and another to read from it in a thread safe manner. Also, allows the reader thread to block waiting for data.
public class ThreadedQueue<T>
{
private readonly Queue<T> _queue = new Queue<T>();
private readonly ManualResetEvent _notEmptyEvt = new ManualResetEvent(false);
public WaitHandle WaitHandle { get { return _notEmptyEvt; } }
public void Enqueue(T obj)
{
lock (_queue)
{
_queue.Enqueue(obj);
_notEmptyEvt.Set();
}
}
public T Dequeue()
{
_notEmptyEvt.WaitOne(Timeout.Infinite);
lock (_queue)
{
var result = _queue.Dequeue();
if (_queue.Count == 0)
_notEmptyEvt.Reset();
return result;
}
}
}
In your serial port handler, write the data into the queue:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string inData = serialPort1.ReadLine();
if (PauseButton.Text == "Pause" && inData.StartsWith("<"))
{
_queue.Enqueue(inData);
}
}
In the secondary thread, read from the queue and do the invoke to the GUI thread:
private void ThreadProc()
{
while (true)
{
string inData = _queue.Dequeue();
this.Invoke(new SetGridDeleg(DoUpdate), new object[] {inData});
}
}
Start up the secondary thread like this:
Thread th = new Thread(ThreadProc);
th.IsBackground = true;
th.Start();
Of course you'll need to create an instance of the queue:
ThreadedQueue<string> _queue = new ThreadedQueue<string>();
I normally design a SerialService class to manage the SerialPort. Below is a simple version of the SerialService class.
The role of the SerialService class is to read the serial buffer as fast as possible. This clears the buffer and prevents any serial port errors. This raw data is then passed to the parser.
The trick for performance is in your parser. YourParser should also be fast in formatting the raw data into the string that you are expecting. Once your data is parse you may use a callback or an event. With a callback or event your parser will continue to parse new arriving data. YourParse is now a testable class.
Once you have your good data from the parser's callback use BeginInvoke to send the data to the main thread where your ui can then display it.
If you are not in the main UI thread and you try to update the UI from another thread you will have the cross theading problem.
Good luck.
class Program
{
private static YourDataParser _parser;
static void Main(string[] args)
{
_parser = new YourDataParser();
var serial = new SerialService("COM1");
serial.DataReceived += serial_DataReceived;
}
static void serial_DataReceived(object sender, DataReceivedEventArgs e)
{
_parser.HandleTheData(e.Data, good =>
{
// here is your good data
// This is not the main thread invoke your UI from here with the good data
// Use BeginInvoke to invoke the main thread
});
}
}
public class YourDataParser
{
private List<byte> _buffer = new List<byte>();
public void HandleTheData(byte[] rawdata, Action<string> goodData)
{
_buffer.AddRange(rawdata);
foreach (var b in _buffer)
{
var thechar = (char) b;
// handle your raw data... like look for the character '<'
// or look for the end of line this would be CR (0x0D) LF (0x0A)
// you can reference the ASCII table for the characters byte values
}
// and return the good data
var data = "your good data after parsing it";
goodData(data);
}
}
public class DataReceivedEventArgs : EventArgs
{
public DataReceivedEventArgs(byte[] data)
{
Data = data;
}
public byte[] Data { get; private set; }
}
class SerialService
{
public event EventHandler<DataReceivedEventArgs> DataReceived;
private SerialPort _port;
public SerialService(string comm)
{
_port = new SerialPort(comm)
{
// YOUR OTHER SETTINGS HERE...
ReceivedBytesThreshold = 1 // I think is better to increase this number if you know the minimum number of bytes that will arrive at the serial port's buffer
};
// Note that the ReceivedBytesThreshold is set to 1.
// That means that the port_DataReceived event will fire with a minimun of 1 byte in the serial buffer
_port.DataReceived += port_DataReceived;
}
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (e.EventType != SerialData.Chars) return;
while (_port.IsOpen & _port.BytesToRead != 0)
{
// important to get all the bytes off the buffer
var size = _port.BytesToRead;
var buffer = new byte[size];
var sizeRead = _port.Read(buffer, 0, size);
OnDataReceived(buffer);
}
}
protected virtual void OnDataReceived(byte[] data)
{
var ev = DataReceived;
if (ev != null) ev(this, new DataReceivedEventArgs(data));
}
}
As you said your code is slowing the data reception.
you can solve your problem by queuing your data to a queue list and a background process will be processing this list one by one.
another approach is to create a new thread on the reception of each data batch.
Example (second approach)
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string inData = serialPort1.ReadLine();
System.Threading.Thread T = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ProcessData));
T.Start(inData);
}
public void ProcessData(Object data)
{
....
}

Wait for DataReceived to fire without blocking the ui

I need to wait for the user to input data to the serialport reader and then process the data. However, using this code blocks the UI which is not what I want. Any ideas on how to make sure that data is received or a timeout has occured before continuing?
The reason I use
do
{
Thread.Sleep(1);
} while (...)
is because without it the code return indata before the user has time to change it.
I call ReadFromSerial from the main function and process the data there. If anything goes wrong I want it to return an empty string.
public string ReadFromSerial()
{
try
{
System.IO.Ports.SerialPort Serial1 = new System.IO.Ports.SerialPort("COM1", 9600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One);
var MessageBufferRequest = new byte[13] { ... };
int BufferLength = 13;
if (!Serial1.IsOpen)
{
Serial1.Open();
}
Serial1.Write(MessageBufferRequest, 0, BufferLength); //Activates the serialport reader
indata = "";
Stopwatch timer = new Stopwatch();
timer.Start();
Serial1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
do
{
Thread.Sleep(1);
} while (string.IsNullOrEmpty(indata) && timer.Elapsed.TotalSeconds < 10);
timer.Stop();
if (Serial1.IsOpen)
{
Serial1.Close();
}
return indata;
}
catch (Exception ex)
{
return "";
}
}
private static string indata;
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
try
{
SerialPort sp = (SerialPort)sender;
if (sp.BytesToRead > 0)
{
indata = sp.ReadExisting();
}
}
catch(InvalidOperationException)
{
;
}
}
This is where multi-threading, tasks, async programming and/or event handlers comes in handy. All of them offer something to help you get around stuff like this, depending on the types of objects you're using.
A good starting point in this case would be to run the whole receive loop as a separate thread, then send the received data back to the main thread in some fashion.
Here's the source of a form that does basically what yours does, but either as a Thread or a Task:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Button: starts Task version
private void button1_Click(object sender, EventArgs e)
{
StartReceiveTask();
}
// Button: starts Thread version
private void button2_Click(object sender, EventArgs e)
{
StartReceiveThread();
}
// Start the Receive loop as a Task
public void StartReceiveTask()
{
System.Threading.Tasks.Task.Run(() => receiveThreadFunc());
}
// Start the Receive loop as a Thread
public void StartReceiveThread()
{
var thd = new System.Threading.Thread(receiveThreadFunc);
thd.Start();
}
// Called when the Receive loop finishes
public void DataReceived(string data)
{
// do something with the data here
}
// The Receive loop, used by both Thread and Task forms.
public void receiveThreadFunc()
{
using (var serial1 = new System.IO.Ports.SerialPort("COM1", 9600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One))
{
// open serial port
if (!serial1.IsOpen)
serial1.Open();
// send init command
var initCommand = new byte[13];
serial1.Write(initCommand, 0, initCommand.Length);
// get start time
DateTime start = DateTime.Now;
// buffer for pushing received string data into
StringBuilder indata = new StringBuilder();
// loop until at most 10 seconds have passed
while ((DateTime.Now - start).TotalSeconds < 2)
{
if (serial1.BytesToRead > 0)
{
// allocate a buffer, up to 1K in length, to receive into
int blen = Math.Min(1024, serial1.BytesToRead);
byte[] buffer = new byte[blen];
// read chunks of data until none left
while (serial1.BytesToRead > 0)
{
int rc = serial1.Read(buffer, 0, blen);
// convert data from ASCII format to string and append to input buffer
indata.Append(Encoding.ASCII.GetString(buffer, 0, rc));
}
}
else
System.Threading.Thread.Sleep(25);
// check for EOL
if (indata.Length > 0 && indata.ToString().EndsWith("\r\n"))
break;
}
if (indata.Length > 0)
{
// post data to main thread, via Invoke if necessary:
string data = indata.ToString();
if (this.InvokeRequired)
this.Invoke(new Action(() => { DataReceived(data); }));
else
this.DataReceived(data);
}
}
}
}
I went with solution not to touch what I had already written. Instead I added these methods in my main function.
private void StartReceiveThread()
{
var thd = new System.Threading.Thread(receiveThreadFunc);
thd.Start();
}
private void receiveThreadFunc()
{
string str = Read.ReadFromSerial();
DataReceived(str);
}
private void DataReceived(string data)
{
//Process the data received
}

How to use Control.Dispatcher.BeginInvoke to modify GUI

I need to modify the GUI from inside of a method that takes long time to finish. As I read other posts,
one of the solution is to use Control.Dispatcher.BeginInvoke to set the GUI inside the worker thread.
However, I don't have a clue how to do this here.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew( () =>
{
ProcessFilesThree();
});
}
private void ProcessFilesThree()
{
string[] files = Directory.GetFiles(#"C:\temp\In", "*.jpg", SearchOption.AllDirectories);
Parallel.ForEach(files, (currentFile) =>
{
string filename = Path.GetFileName(currentFile);
// the following assignment is illegal
this.Text = string.Format("Processing {0} on thread {1}", filename,
Thread.CurrentThread.ManagedThreadId);
});
this.Text = "All done!"; // <- this assignment is illegal
}
}
Try the following:
msg = string.Format("Processing {0} on thread {1}", filename,
Thread.CurrentThread.ManagedThreadId);
this.BeginInvoke( (Action) delegate ()
{
this.Text = msg;
});
private void ProcessFilesThree()
{
// Assuming you have a textbox control named testTestBox
// and you wanted to update it on each loop iteration
// with someMessage
string someMessage = String.Empty;
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000); //Simulate a second of work.
someMessage = String.Format("On loop iteration {0}", i);
testTextBox.Dispatcher.BeginInvoke(new Action<string>((message) =>
{
testTextBox.Text = message;
}), someMessage);
}
}

Categories

Resources