views:

206

answers:

7

Background tasks being stuff that involves network I/O, disk I/O, or other long-running tasks that may or may not take place over a network. It will often intermix with code that updates the GUI, which needs to be running on another thread, the GUI thread.

Simple meaning that when opening a Form.cs file, the source code is as easy or easier to read than before. In practice, the flow of the source code must still read sequentially with regards to the order in which the code is executed, regardless of which thread it executes on. All the supporting fabric must be reusable and hidden away somewhere, rather than being included in each Form.

Googling MSDN: found that the solution officially sanctioned by Microsoft is the System.ComponentModel.BackgroundWorker, which falls (very!) short on the second point.

(There's also an officially sanctioned Silverlight/XAML/3.5 solution model in System.Windows.Threading.Dispatcher.)

+2  A: 

You can still use the BackgroundWorker. It does not need to live as a component on the form. You can just as easily wrap it into a class that can then be reused in each of the forms.

However, this will be little different from simply setting up the worker for the background task when needed.

Fredrik Mörk
not sure what you mean by the last paragraph.
rosenfield
@rosenfield: if you wrap handling background tasks into a separate class, you will still need to create an instance, launch the task and take care of any result in some way. This is pretty much what `BackgroundWorker` provides.
Fredrik Mörk
@fredrik: BGW is nasty in exactly that way, because it forces you to create a new BGW instance and write new BGW event handlers every time you have another task to be backgrounded, in effect polluting every event handler that needs to background a task with a huge amount of setup etc.
rosenfield
+2  A: 

Use BackgroundWorker for that

Tadas
Read the original question.
rosenfield
+1  A: 

Can you explain why you say the BackgroundWorker falls short?

In most cases it requires 2-3 extra lines of code.

leppie
yes (hopefully): it is not only a matter of how many extra lines of code is necessary, but also the problem with BackgroundWorker is that it makes the original single-threaded code unreadable.BackgroundWorker forces you to divide logical and coherent code into a bunch of event handlers and different methods.
rosenfield
The BG-worker inherits from the component base because it then knows to synchronize with it's owner control, for cross-thread safe calls. That's why it 'forces' you into event handlers methods.If you don't need thread safety you can always implement your own threaded class using the threading.threadpool class
Wez
@kb: regarding "knows to synchronize", is it doing anything else/smarter than Invoke() et al?
rosenfield
+2  A: 

If you really don't like BackgroundWorker, you could create your own base class for background actions, as I did here.

Mark Heath
Nice interface implementation!
Wez
That's really nice!<br />It has a few details that are counter to what I need: - it imposes a strict API onto how progress reporting is done. I would rather leave it up to the developer to decide what UI parts needs updating etc.<br /> - it still needs too much code for each "thing" that needs to be backgrounded. it must be just as (or almost as) easy to do backgrounded as single threading, otherwise people won't do it.<br />But also there are major advantages to your approach, such as how easy it is to plug it into a ProgressForm. Nice.
rosenfield
I don't really see what this interface provides that is not provided by `BackgroundWorker` (except requiring the UI code to check `InvokeRequired` since the events are raised *on the worker thread* instead of being raised *on the thread that created the worker*, as the `BackgroundWorker` does)?
Fredrik Mörk
In your example you should use, if you don´t like BackgroundWorker, an instance of AsyncOperation (created in the UI thread) to raise the events on the UI thread. So the UI code has not to check for InvokeRequired.
Jehof
Sure, its not perfect, and I'd do it different if I did it again. I would probably also use SynchronizationContext.Current to avoid making my BackgroundActions dependent on WinForms. The InvokeRequired went into my "ProgressForm" in this particular application, so the UI code was not repeated. Background worker has the unfortunate effect of encouraging people to write the background action in their code behind, whereas an abstract base class forces people to create a new class.
Mark Heath
A: 

This is the simplest idea I've come up with so far. It may be entirely un-kosher, I've coded incredibly close to zero Windows.Forms applications.

It involves a helper which has two main methods, Background() and Foreground(). Either of these take a delegate specified in the form of a lambda expression. Background() starts any given delegate on a background thread and returns immediately. Foreground() sends any given delegate "back" to the GUI thread using Form.BeginInvoke() and returns immediately.

In the following is a code example of how to use this design pattern, provided that the helper is already implemented.

public class Form1 : Form {
    protected ProgressBar progressBar1;
    protected Button button1;

    protected BackgroundHelper helper = new BackgroundHelper();

    public void button1_Click(...) {
     // Execute code in the background.
     helper.Background(() => {
      for (int i = 0; i <= 100; i++) {
       // Continually report progress to user.
       helper.Foreground<int>(i, j => {
        progressBar1.Value = j;
       });
       // Simulate doing I/O or whatever.
       Thread.Sleep(25);
      }
     });
    }
}

This keeps the code neatly sequential, provides shared variables in a good place, and allows loops that span the two threads.

To clarify what the helper does,

  • The constructor starts a background thread that waits on a queue.
  • Both Background() and Foreground() returns immediately.
  • Background() enqueues code to run in the background thread using the internal queue.
  • Foreground() does the same, with BeginInvoke on the GUI thread that first created the helper.

EDIT: Implementation:
http://code.google.com/p/backgrounder/

rosenfield
A: 

Following your question of Disk I/O for example, I wrote an asynchronous File-scanner class recently (or the 100th time over all the years). It's re-usable like you want, by just instantiating a new instance and hooking into the events.

I encapsulated the functionality inside a class that implements cross-thread safety. the class fires off events to notify the caller of updates and when it's complete.

You say that "the source code must still read sequentially with regards to the order in which the code is executed", but realize that threads run parallel to each other. That's why its good to separate the code structures.

This demonstrates isolating reusable code into a separate class...

Public Class FileScanner
    Public Event Scan_Complete(sender As Object)
    Public Event Scan_Update(sender As Object, filename As String)
    Public Event Scan_Error(sender As Object, ex As Exception)
    Private Delegate Sub del_ScanComplete()

    Sub New(syncObject As Control, path String)
     Me.SynchronizeObject = syncObject
     ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ScanFilesAsync), path)
    End Sub

    Private Sub ScanFilesAsync(ByVal state As Object)

     ' scan files here

     ' call the method to raise the Complete event
     ScanComplete()

    End Sub

    Private Sub ScanComplete()

     If SynchronizeObject.InvokeRequired Then
      ' we cant raise event on a different thread than our caller
      ' so lets invoke our method on the same caller thread
      Dim d As New del_ScanComplete(AddressOf ScanComplete)
      SynchronizeObject.Invoke(d)
     Else
      ' no synchronize needed, tell the caller we are done
      RaiseEvent Complete(Me)
     End If

    End Sub

End Class
Wez
Hmm, regarding "but realize that threads run parallel to each other. That's why its good to separate the code structures.", I disagree.It's not completely parallel, because Invoke() does PostMessage() and that serializes the code to such a degree that splitting just accomplishes making it harder to read what's going on.
rosenfield
the code example is for a specific background action (scanning files), I'm looking for something that is plug-and-play reusable for everything.
rosenfield
A: 

An alternativ for doing background tasks in Windows Forms and raising events when they are done, is to use the AsyncOperationManager and AsyncOperation classes. To note is here that the AsyncOperation has to be created in the UI thread to work properly. Otherwise the UI code has to check for InvokeRequired.

public class BackgroundTask{

  private AsyncOperation _asyncOperation;
  public EventHandler Done;

  public BackgroundTask(){
    _asyncOperation = AsyncOperationManager.CreateOperation();
  }

  public void DoAsync(object userState) {
    System.Threading.ThreadPool.QueueUserWorkItem( ExecuteDo, userState);
  }

  private void ExecuteDo(object state) {
    // Do your work here
    // Raise event after finish
    _asyncOperation.PostOperationCompleted( Finished, EventArgs.Empty );
  }

  private void LookUpFinished( object eventargs ) {
    OnDone( ( EventArgs) eventargs );
  }

  private void OnDone( LookUpEventArgs e ) {
    EventHandler localEvent = Done;
    if ( localEvent!= null ) {
      localEvent(this,e);
    } 
  }
}

Here´s the code how to use it:

public class MyForm : Form {

  public MyForm() {
    InitializeComponent();
  }

  protected override OnShown(EventArgs e) {
    BackgroundTask task = new BackgroundTask();
    task.Done += SignalTaskDone;
  }

  private void SignalTaskDone(object sender, EventArgs e){
    MessageBox.Show(this, "Task finished");
  }
}
Jehof
In actual usage, you will still end up dividing any code using the above into tiny little pieces, because this is basically a callback-/event-based asynchronous design pattern just like BGW is. (And is therefore just as unsuitable, unfortunately.)
rosenfield