SimpleOne
SimpleOne

Reputation: 1076

C# multithreading - updating the GUI with background events

I'm a newbie to C# and multithreading so I apologise if this is a duplicate question but being a novice it appears my question is slightly different to others I have read.

My GUI runs in one (main) thread. It calls a background task (in a dll -- that I am writing too) that runs in a separate thread. The dll has NO knowledge of the GUI (i.e. it cannot reference the GUI class).

Now, say I want to update a progress bar on the GUI based on the status of the dll thread -> What I'm doing is creating an event in the dll that every X per cent will fire and the GUI will subscribe to this event. When the event is fired, the GUI will update the progress bar.

My questions:

  1. Is the method of creating events the best way (bearing in mind the dll cannot reference the GUI)?
  2. How do I ensure my above method is 'event safe'? Should I pass the progress percentage in the event to be thread safe or is there more to it?
  3. Do I need to use Invoke when updating the GUI? I saw a post that suggested I did but I don't understand why since the updating of the bar is being done in the GUI thread??!

Hope you can clarify this for me!

Thanks

Upvotes: 5

Views: 6971

Answers (6)

Scott Chamberlain
Scott Chamberlain

Reputation: 127543

1.-I use that method all the time and yes it will work

2.-Just pass a int to the event handler and the variable will be safe to read. however when you are fireing the event from code do it like this

private void UpdatePercentage(int a)
{
    var myEvent = PercentageUpdatedEvent
    if(myEvent != null)
         myEvent(this, new ProgressBarEventArgs(a));
}

The reason for this is so if the event is unsubcribed between the null check and the calling you won't get a exception.

3.-As everyone else has mentioned you will need to call Invoke as the event will be running on the dll's thread. However with controls it is legal to call a BeginInvoke without a EndEnvoike so the call will be non blocking on the dll's thread.

Here is the pattern I always use

private myClass_OnPercentageUpdatedEvent(object a, ProgressBarEventArgs e)
{
    if(progressBar.InvokeRequired)
        progressBar.BeginInvoke((Action<object,ProgressBarEventArgs>)myCless_OnPercentageUpdatedEvent, a, e);
    else
    {
        progressBar.Value = e.Value;
    }
}

Upvotes: 3

Stephen Cleary
Stephen Cleary

Reputation: 456417

I have on my blog a few different approaches to this problem, with the advantages/disadvantages of each. In summary, I recommend using the Task class.

Upvotes: 0

George Johnston
George Johnston

Reputation: 32258

If you spin off a thread, you need to create a delegate, that can safely invoke your main thread with the appropriate parameters.

delegate void UpdateDelegate(int val)
void Update(int val)
{
  if(this.InvokeRequired())
  {
     Invoke(new UpdateDeleage(Update),new object[] {val});
     return;
  }
  this.MyProgressBar.Value = val;
}

Call Update from your separate thread as you would if calling it from your main thread. Once the thread determines that your main thread needs invoked to pass the value, it will invoke it with your delegate, with the parameters you passed. Otherwise, it will simply skip the block and set your values.

e.g.

...

new Thread(()=>IncrementValues()).Start();

...

void IncrementValues()
{
   while(true)
   Update(new Random(0,10));
}

Upvotes: 0

Andrew Barber
Andrew Barber

Reputation: 40149

Keep in mind that under most circumstances, the events raised from your background task will also run on the background thread. No thread context switch happens automatically at all.

To understand why, you have to consider what an event is; just a certain type of Delegate object. You are setting a Delegate to that event from the main thread... but that delegate will actually be called within the background thread, in the code that triggers the event.

So yes; you would need to make sure you are moving things over to run on the GUI thread from within that event handler.

Upvotes: 3

Paul Phillips
Paul Phillips

Reputation: 6259

To answer (3) you will need to use Invoke. The event-handlers are going to be run from the background thread, not the GUI thread.

Upvotes: 0

Dismissile
Dismissile

Reputation: 33071

Look into the BackgroundWorker class. It sounds like it fits your scenario pretty well.

This link on MSDN explains how to use it: http://msdn.microsoft.com/en-us/library/cc221403%28VS.95%29.aspx

Upvotes: 3

Related Questions