views:

387

answers:

4

I have a question about events in .NET (C#). I have had to write code for several situations in which I have a background task running and I want to notify a main thread, or controller class, that something has occurred, such as task completed, or finished copying a file, but I don't want the background task to wait for the main thread's delegate to process the event.

I want to do something like message passing: Send the message, but who cares what they do with it.

For example:

A class is written to process several processes in sequence, one after the other. Each process needs to run on a background thread. When work is completed, an event fires and tells the controller that it's done (let's say using OnCompleted() method)

The problem is that if the controller's event handler is used to start the subsequent process, the previous processes' OnComplete method stays on the call stack (never finishes executing) until the all of the processes have completed.

In that situation, how could the background task notify the controller class that the work is done without keeping the event raising method on the stack?

Example 2: A backup program.

A background thread runs to copy each file to the destination. The background needs to notify the UI the last file that was copied, but it doesn't need to wait for the UI to update. Instead, it just wants to say, "BTW, here's some info. Now, let me get back to work." The event listener shouldn't block the processing of the event raiser.

A: 

Have the first event do nothing but kick off the thread, then it doesn't matter what other event listeners there are.

Luke Schafer
How would I do that in C#?
Chris Thompson
well, you seem to know how to kick off another thread. Just have the first event listener do this (as soon as you create the object with the event, assign the handler
Luke Schafer
+2  A: 

It sounds like you are trying to invoke the delegates in the event's invocation list asynchronously.

I would suggest that you read .NET Asynchronous Events To Send Process Status To User Interface:

The .NET Framework offers us the concept of raising events (and other items) in our classes asynchronously. This means that we can raise the event in such a way as to not have the subscriber to that event (typically the user interface) hold up the processing in the method that raised the event. The benefit being that it doesn't negatively impact the performance of our business layer method.

Andrew Hare
I had read about the BeginInvoke/EndInvoke pattern, but I was thinking about using on that on the listener side, not on the throwing side.Could there be an issue if there were tons of outstanding BeginInvoke calls?
Chris Thompson
+1  A: 

You could do an async invoke when raising the event (as mentioned), or just raise the event itself on a background thread:

void OnUpdated(EventArgs e) {
   EventHandler h = this.Updated;
   if (h != null) h(e);
}

void DoStuff() {
   BigMethod();
   ThreadPool.QueueUserWorkItem(OnUpdated, EventArgs.Empty);
   BigMethod2();
}

If you raise asynchronously, multiple listeners would be processing your event at the same time. At the very least, that requires a thread-safe EventArg class. If you expect them to interact with your class as well, then you should document very carefully or make it thread-safe as well.

Raising the event on a background thread carries the same caveats for your class methods, but you don't have to worry about the EventArgs class itself.

Mark Brackett
A: 

For your case 2 of back up program. The code sample will fire a file copy asynchronously and once the copy is done it calls the call back method. In the callback if you dont want to wait for UI to update, then you will have to call the UI updating code asynchronously

You can use asynchronous delegates

public class AsyncFileCopier
    {
        public delegate void FileCopyDelegate(string sourceFile, string destFile);

        public static void AsynFileCopy(string sourceFile, string destFile)
        {
            FileCopyDelegate del = new FileCopyDelegate(FileCopy);
            IAsyncResult result = del.BeginInvoke(sourceFile, destFile, CallBackAfterFileCopied, null);
        }

        public static void FileCopy(string sourceFile, string destFile)
        { 
            // Code to copy the file
        }

        public static void CallBackAfterFileCopied(IAsyncResult result)
        {
            // Notify UI by calling an async del (probably using fire & forget approach or another callback if desired)
        }
    }

You can call it as:

AsyncFileCopier.AsynFileCopy("abc.txt", "xyz.txt");

This link tells you the different techniques of asyn coding

Rashmi Pandit