[ACCEPTED]-In C# what is the recommended way of passing data between 2 threads?-multithreading
Wow, I can't believe how may people didn't 15 bother reading the question.
Anyways, this 14 is what I do.
- Create you "message" classes. This stores all the information you want to share.
- Create a Queue<T> for each thread. Use a SyncLock (C# lock) to read/write to it.
- When you want to talk to a thread, send it a message object with a copy of all the information it needs by adding the message to the queue.
- The worker thread can then read from the queue, reading and processing each message in order. When there are no messages, simply sleep.
Make sure that you don't share 13 objects between the two threads. Once your 12 GUI thread sticks a message in the Queue, the 11 GUI thread no longer owns the message. It 10 cannot hold a reference to the message, or 9 you will get yourself into trouble.
This 8 won't give you the best possible performance, but 7 it will be good enough for most applications. And 6 more importantly, it will make it much harder 5 to make a mistake.
UPDATE: Don't use a SyncLock 4 and Queue. Instead use a ConcurrentQueue, which 3 will handle any locking automatically for 2 you. You'll get better performance and are 1 less likely to make a mistake.
.Net already comes with a System.ComponentModel.BackgroundWorker
class specifically 2 to handle performing background tasks and 1 communicating with a GUI. Use it.
Dude, read Albahari's .Net threading free 8 ebook. I'm connected to it by any means, so 7 this is no plug. I've read it, had my coworkers 6 read it, and i've used it many times.
I would 5 recommend creating a producer/consumer class, where 4 you can start a single thread that waits 3 (non blocking), enqueue tasks to its queue, and 2 signal it to start working.
just google for 1 it.
In effect, you have created a poor man's 9 version of a ThreadPool. Your second thread 8 is just sitting there doing nothing and 7 without a fair amount of work on your part, you 6 can't just get it to do work for you. You 5 would have to pass delegates into a queue 4 that your thread then takes off and executes.
Your 3 best bet is to do what you intended and 2 just use the .NET ThreadPool and give it 1 work to do.
I'm assuming some event in the GUI requires 16 some long-running task to start which be 15 run in the background - there are two main 14 ways to do this. If you simply want to call 13 a method on a different thread then you 12 can do it by Calling Synchronous Methods Asynchronously. I usually do something like 11 this:
//delegate with same prototype as the method to call asynchrously
delegate void ProcessItemDelegate(object item);
//method to call asynchronously
private void ProcessItem(object item) { ... }
//method in the GUI thread
private void DoWork(object itemToProcess)
{
//create delegate to call asynchronously...
ProcessItemDelegate d = new ProcessItemDelegate(this.ProcessItem);
IAsyncResult result = d.BeginInvoke(itemToProcess,
new AsyncCallback(this.CallBackMethod),
d);
}
//method called when the async operation has completed
private void CallbackMethod(IAsyncResult ar)
{
ProcessItemDelegate d = (ProcessItemDelegate)ar.AsyncState;
//EndInvoke must be called on any delegate called asynchronously!
d.EndInvoke(ar);
}
Be aware when using this method that 10 the callback is executed on the background 9 thread, so any updates to the GUI must be 8 done using Invoke.
Alternatively you could 7 use shared state to communicate between 6 threads and use an EventWaitHandle to signal updates to 5 the shared state - in this example a method 4 in the GUI adds work items to a queue to 3 be handled in the background. The worker 2 thread processes items from the queue when 1 work becomes available.
//shared state
private Queue workQueue;
private EventWaitHandle eventHandle;
//method running in gui thread
private void DoWork(Item itemToProcess)
{
//use a private lock object instead of lock...
lock(this.workQueue)
{
this.workQueue.Add(itemToProcess);
this.eventHandle.Set();
}
}
//method that runs on the background thread
private void QueueMonitor()
{
while(keepRunning)
{
//if the event handle is not signalled the processing thread will sleep here until it is signalled or the timeout expires
if(this.eventHandle.WaitOne(optionalTimeout))
{
lock(this.workQueue)
{
while(this.workQueue.Count > 0)
{
Item itemToProcess = this.workQueue.Dequeue();
//do something with item...
}
}
//reset wait handle - note that AutoResetEvent resets automatically
this.eventHandle.Reset();
}
}
}
The convenience of Control.BeginInvoke() is 3 hard to pass up. You don't have to. Add 2 a new class to your project and paste this 1 code:
using System;
using System.Threading;
using System.Windows.Forms;
public partial class frmWorker : Form {
public frmWorker() {
// Start the worker thread
Thread t = new Thread(new ParameterizedThreadStart(WorkerThread));
t.IsBackground = true;
t.Start(this);
}
public void Stop() {
// Synchronous thread stop
this.Invoke(new MethodInvoker(stopWorker), null);
}
private void stopWorker() {
this.Close();
}
private static void WorkerThread(object frm) {
// Start the message loop
frmWorker f = frm as frmWorker;
f.CreateHandle();
Application.Run(f);
}
protected override void SetVisibleCore(bool value) {
// Shouldn't become visible
value = false;
base.SetVisibleCore(value);
}
}
Here's some sample code to test it:
public partial class Form1 : Form {
private frmWorker mWorker;
public Form1() {
InitializeComponent();
mWorker = new frmWorker();
}
private void button1_Click(object sender, EventArgs e) {
Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
mWorker.BeginInvoke(new MethodInvoker(RunThisOnThread));
}
private void RunThisOnThread() {
Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
}
private void button2_Click(object sender, EventArgs e) {
mWorker.Stop();
}
}
Use a synchronization object to signal the 5 thread that it needs to process the new 4 data (or the GUI's new state). One relatively 3 simple way to do this is to use an event 2 object. Here's a run-down of how that would 1 work:
- GUI thread an 2nd thread share an event object (so they both know about it)
- 2nd thread typically runs in a loop of some sort, and each time it waits for the event to be signaled
- GUI thread signals the event when it needs the 2nd thread to do something
- When the 2nd thread is done, it resets the event and waits again (or exits)
Put a loop in your second thread, that sleeps 15 most of the time, but every [Interval] it 14 wakes up and checks a shared variable that 13 tells it whether to run your method or not, and 12 if that shared boolean is set to true, then 11 it runs a method that performs whatever 10 task you are trying to perform... In that 9 method, have the method gather the data 8 required from another shared variable.
In 7 main GUI thread, put the data into the method 6 parameter shared variable, then set the 5 boolean "Run" shared variable to true...
Inside 4 the worker method, remember to reset the 3 shared bool "run" variable to false when 2 you're done, so the loop won;t run the same 1 instance over and over...
You can use events or as Grauenwolf said 7 - a message cue. I wrap each of my thread 6 as a management singleton, from there you 5 can implement either easily. You could 4 even do poor man public properties to flip 3 bits.
Also you could implement a state machine, and 2 instead of pass messages each thread could 1 monitor each other
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.