views:

2883

answers:

7

What is the proper technique to have ThreadA signal ThreadB of some event, without having ThreadB sit blocked waiting for an event to happen?

i have a background thread that will be filling a shared List<T>. i'm trying to find a way to asynchronously signal the "main" thread that there is data available to be picked up.


i considered setting an event with an EventWaitHandle object, but i can't have my main thread sitting at an Event.WaitOne().


i considered having a delegate callback, but a) i don't want the main thread doing work in the delegate: the thread needs to get back to work adding more stuff - i don't want it waiting while the delegate executes, and b) the delegate needs to be marshalled onto the main thread, but i'm not running a UI, i have no Control to .Invoke the delegate against.


i considered have a delegate callback that simply starts a zero interval System.Windows.Forms.Timer (with thread access to the timer synchronized). This way the thread only needs to be stuck as it calls

Timer.Enabled = true;

but that seems like a hack.

In the olden days my object would have created a hidden window and had the thread post messages to that hidden windows' HWND. i considered creating a hidden control, but i gather that you cannot .Invoke on a control with no handle created. Plus, i have no UI: my object could have been created on a web-server, service, or console, i don't want there to be a graphical control appearing - nor do i want to compile a dependency on System.Windows.Forms.


i considered having my object expose an ISynchronizeInvoke interface, but then i would need to implement .Invoke(), and that's my problem.


What is the proper technique to have thread A signal thread B of some event, without having thread B sit blocked waiting for an event to happen?

+1  A: 

If you use a backgroundworker to start the second thread and use the ProgressChanged event to notify the other thread that data is ready. Other events are available as well. THis MSDN article should get you started.

Mitchel Sellers
The BackgroundWorker class seems to be the only thing that can asynchronously sent a notification to the *thread* that created the object. Internally it uses the AsyncOperation object by calling asyncOperation.Post().
Ian Boyd
+1  A: 

There are many ways to do this, depending upon exactly what you want to do. A producer/consumer queue is probably what you want. For an excellent in-depth look into threads, see the chapter on Threading (available online) from the excellent book C# 3.0 in a Nutshell.

Kris Erickson
In producer/consumer model the main thread creates units of work that need to be performed. A background thread sits waiting for work be queued up. When a unit of work is created and event is set. That doesn't work here since the main thread is the "consumer", and it can't sit waiting for work.
Ian Boyd
+1  A: 

You can use an AutoResetEvent (or ManualResetEvent). If you use AutoResetEvent.WaitOne(0, false), it will not block. For example:

AutoResetEvent ev = new AutoResetEvent(false);
...
if(ev.WaitOne(0, false)) {
  // event happened
}
else {
 // do other stuff
}
Leandro Oliveira
How do you check if the event is signalled? i.e. when?
Ian Boyd
A: 

If your "main" thread is the Windows message pump (GUI) thread, then you can poll using a Forms.Timer - tune the timer interval according to how quickly you need to have your GUI thread 'notice' the data from the worker thread.

Remember to synchronize access to the shared List<> if you are going to use foreach, to avoid CollectionModified exceptions.

I use this technique for all the market-data-driven GUI updates in a real-time trading application, and it works very well.

McKenzieG1
+2  A: 
OwenP
Queue seems like an interesting class to use. Although i wouldn't want it in my situation since sometime the user can handle the entire List at once, rather than having to Dequeue individual items.
Ian Boyd
+8  A: 

Here's a code sample for the System.ComponentModel.BackgroundWorker class.

    private static BackgroundWorker worker = new BackgroundWorker();
    static void Main(string[] args)
    {
        worker.DoWork += worker_DoWork;
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        worker.ProgressChanged += worker_ProgressChanged;
        worker.WorkerReportsProgress = true;

        Console.WriteLine("Starting application.");
        worker.RunWorkerAsync();

        Console.ReadKey();
    }

    static void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        Console.WriteLine("Progress.");
    }

    static void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        Console.WriteLine("Starting doing some work now.");

        for (int i = 0; i < 5; i++)
        {
            Thread.Sleep(1000);
            worker.ReportProgress(i);
        }
    }

    static void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Console.WriteLine("Done now.");
    }
herbrandson
+1  A: 

The BackgroundWorker class is answer in this case. It is the only threading construct that is able to asynchronously send messages to the thread that created the BackgroundWorker object. Internally BackgroundWorker uses the AsyncOperation class by calling the asyncOperation.Post() method.

this.asyncOperation = AsyncOperationManager.CreateOperation(null);
this.asyncOperation.Post(delegateMethod, arg);

A few other classes in the .NET framework also use AsyncOperation:

  • BackgroundWorker
  • SoundPlayer.LoadAsync()
  • SmtpClient.SendAsync()
  • Ping.SendAsync()
  • WebClient.DownloadDataAsync()
  • WebClient.DownloadFile()
  • WebClient.DownloadFileAsync()
  • WebClient...
  • PictureBox.LoadAsync()
Ian Boyd