5

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

3
  • What do you mean by 'event safe'? Commented Oct 26, 2010 at 16:55
  • See the comments on the BackgroundWorker class here: stackoverflow.com/questions/1506838/… Commented Oct 26, 2010 at 18:24
  • I'd just like to say thanks for ALL the replies. They've all helped with answering my questions. Great to see such help! Commented Oct 26, 2010 at 19:34

6 Answers 6

3

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;
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This really helped. Many thanks. It finally clicked (probably obvious to all of you) that even though the event handler is written in the GUI class, it is still running in the dll thread as that where it is 'called' from! Now I get why we need Invoke and this clarifies it. I agree with other posts that I needed to understand why/how it works rather than use BackgroundWorker, etc.
3

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

2 Comments

The BackgroundWorker is trying help you solve the task of running background operations without requiring any real knowledge about how threads and the UI work, but isn't really offering much help if you do understand how events are raised and how to use the Dispatcher. I would highly recommend anyone that wants to do multi threaded code to at least attempt to understand what's going on under the hood of an abstraction such as the background worker.
I agree that it's important to understand what is going on underneath the hood, but I think that BackgroundWorker is probably the safest way to implement it if you don't understand what is going on underneath the hood.
3

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.

1 Comment

That nails it: "but that delegate will actually be called within the background thread, in the code that triggers the event."
0

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.

Comments

0

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));
}

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.