views:

146

answers:

7

What do I want to achieve: I want to perform some time consuming operations from my MDI winforms application (C# - .NET).

An MDI child form may create the thread with the operation, which may take long time (from 0.1 seconds, to even half hour) to complete. In the meantime I want the UI to respond to user actions, including manipulation of data in some other MDI child form. When the operation completes, the thread should notify the MDI child that the calculations are done, so that the MDI child can perform the post-processing.

How can I achieve this: Should I use explicit threading (i.e., create explicit threads), thread pools? Or simply just propose your solution. Should I create foreground or background threads?

And how does the thread communicates with the GUI, according the solution you propose?

If you know of a working example that handles a similar situation, please make a note.

+3  A: 

Use the BackgroundWorker class. It does just what you're looking for:

        BackgroundWorker backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += new DoWorkEventHandler(LongRunningCode);
        backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(CallbackOnCompletion);
        backgroundWorker.RunWorkerAsync();

Also, to update your UI from the background thread, you need to call Invoke on your UI element.

BFree
`BackgroundWorker` raises `RunWorkerCompleted` and `ProgressChanged` in the UI thread.
Wilhelm
+5  A: 

I think you are looking for the BackgroundWorker class. Example

m0sa
A: 

If you have a very finite number of threads (let's say, at most 4), then you can create them yourself. If you need them to be managed in a way that you don't want to deal with it, then a threadpool is one option or .NET 4.0's System.Threading.Tasks namespace.

You'll probably need to use delegates. Here's an example of one that writes to the UI and one that reads from the UI:

    delegate void SetResultsTextCallback(string text);
    delegate string GetIterationCountCallback();

    private void SetResultsText(string text)
    {
        if (this.ResultsBox.InvokeRequired)
        {
            var d = new SetResultsTextCallback(SetResultsText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.ResultsBox.Text = text;
        }
    }

    private string GetIterationCount()
    {
        if (this.RepetitionSelector.InvokeRequired)
        {
            var del = new GetIterationCountCallback(GetIterationCount);
            IAsyncResult result = del.BeginInvoke(null, null);
            return del.EndInvoke(result);
        }
        else
        {
            return RepetitionSelector.SelectedItem.ToString();
        }
    }

In this example, ResultsBox is a TextBox and RepetitionSelector is a combobox.

Jaxidian
+2  A: 

This depends, slightly.

In general, I agree with BFree that the BackgroundWorker is likely the best option here. I makes notification back to the UI simple, etc.

That being said, the only reason I'm posting, and questioning whether you may want to use BackgroundWorker is this statement:

which may take long time (from 0.1 seconds, to even half hour) to complete

BackgroundWorker uses a ThreadPool thread to perform its processing. This means that closing your Application form will terminate the thread, since it's a background thread.

If your "half hour" processing "work" is something that you would like to have continue operating, even if the form is closed, you may want to make your own foreground thread to perform this operation, and handle the UI marshaling yourself.

Reed Copsey
Each mdi child form, carries its own data, so when the user wants to close it, the operation should be canceled somehow. Also, considering that the user may have some child forms and some of them may perform such time comsuming calculations, as I explain above, what is the best way to implement this, in your opinion?
tmarouda
Is .NET 4 (or the Reactive Framework) an option? If so, I'd use a Task flagged with LongRunning, and a CancellationToken. That's a much cleaner API for handling this. Otherwise, a BackgroundWorker is probably fine - but you'll need to handle cancellation yourself...
Reed Copsey
+2  A: 

While the BackgroundWorker object is an obvious choice, it may not be advisable for a lengthy process, as it uses the ThreadPool. The conventional wisdom around the ThreadPool is that it shouldn't be used for long-running tasks; in these cases explicit thread creation is advisable.

You can still interact with the GUI by calling Invoke or BeginInvoke on the form (or any of its controls), passing in a delegate that will perform the GUI-related actions.

Adam Robinson
+1  A: 

You have many options:

  1. Use BackgroundWorker directly which basically does most of the work for you, but the lengthy operation must be known by the UI.
  2. Inherit from System.ComponentModel.BackgroundWorker which enables to use the operation as a component and separate the logic from the UI.
  3. Use the Event-based pattern, which would allow greater control for the parameters and events raised.
  4. Use the delegates, if there is really nothing to report but the ned of the operation. You must have caution as the callback method is not guaranteed to run in the UI thread.
Wilhelm
A: 

I recommend creating a thread and using a callback method to communicate with the UI.

I give an example in this solution, where you'll also find examples for other approaches: http://stackoverflow.com/questions/2282206/net-threading-locks-waiting/2283163#2283163.

ebpower