Search code examples
c++winformsmultithreadingc++-clibackgroundworker

Some questions on Multithreading and Background worker threads in windows form


I have encountered the need to use multithreading in my windows form GUI application using C++. From my research on the topic it seems background worker threads are the way to go for my purposes. According to example code I have

System::Void backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) 
{
    BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
    e->Result = SomeCPUHungryFunction( safe_cast<Int32>(e->Argument), worker, e );
}

However there are a few things I need to get straight and figure out

  • Will a background worker thread make my multithreading life easier?
  • Why do I need e->Result?
  • What are the arguments passed into the backgroundWorker1_DoWork function for?
  • What is the purpose of the parameter safe_cast(e->Argument)?
  • What things should I do in my CPUHungryFunction()?
  • What if my CPUHungryFunction() has a while loop that loops indefinitely?
  • Do I have control over the processor time my worker thread gets?
  • Can more specifically control the number of times the loop loops within a set period? I don’t want to be using up cpu looping 1000s of times a second when I only need to loop 30 times a second. *Is it necessary to control the rate at which the GUI is updated?

Solution

  • Will a background worker thread make my multithreading life easier?

    Yes, very much so. It helps you deal with the fact that you cannot update the UI from a worker thread. Particularly the ProgressChanged event lets you show progress and the RunWorkerCompleted event lets you use the results of the worker thread to update the UI without you having to deal with the cross-threading problem.

    Why do I need e->Result?

    To pass back the result of the work you did to the UI thread. You get the value back in your RunWorkerCompleted event handler, e->Result property. From which you then update the UI with the result.

    What are the arguments passed into the function for?

    To tell the worker thread what to do, it is optional. Otherwise identical to passing arguments to any method, just more awkward since you don't get to chose the arguments. You typically pass some kind of value from your UI for example, use a little helper class if you need to pass more than one. Always favor this over trying to obtain UI values in the worker, that's very troublesome.

    What things should I do in my CPUHungryFunction()?

    Burn CPU cycles of course. Or in general do something that takes a long time, like a dbase query. Which doesn't burn CPU cycles but takes too long to allow the UI thread to go dead while waiting for the result. Roughly, whenever you need to do something that takes more than a second then you should execute it on a worker thread instead of the UI thread.

    What if my CPUHungryFunction() has a while loop that loops indefinitely?

    Then your worker never completes and never produces a result. This may be useful but it isn't common. You would not typically use a BGW for this, just a regular Thread that has its IsBackground property set to true.

    Do I have control over the processor time my worker thread gets?

    You have some by artificially slowing it down by calling Thread.Sleep(). This is not a common thing to do, the point of starting a worker thread is to do work. A thread that sleeps is using an expensive resource in a non-productive way.

    Can more specifically control the number of times the loop loops within a set period? I don’t want to be using up cpu looping 1000s of times a second when I only need to loop 30 times a second.

    Same as above, you'd have to sleep. Do so by executing the loop 30 times and then sleep for a second.

    Is it necessary to control the rate at which the GUI is updated?

    Yes, that's very important. ReportProgress() can be a fire-hose, generating many thousands of UI updates per second. You can easily get into a problem with this when the UI thread just can't keep up with that rate. You'll notice, the UI thread stops taking care of its regular duties, like painting the UI and responding to input. Because it keeps having to deal with another invoke request to run the ProgressChanged event handler. The side-effect is that the UI looks frozen, you've got the exact problem back you were trying to solve with a worker. It isn't actually frozen, it just looks that way, it is still running the event handler. But your user won't see the difference.

    The one thing to keep in mind is that ReportProgress() only needs to keep human eyes happy. Which cannot see updates that happen more frequently than 20 times per second. Beyond that, it just turns into an unreadable blur. So don't waste time on UI updates that just are not useful anyway. You'll automatically also avoid the fire-hose problem. Tuning the update rate is something you have to program, it isn't built into BGW.