2

I have an assembly (.NET class library) that contains an object that implements EAP for asynchronous methods:

class Product
{
    void Install();
    void InstallAsync();
    EventHandler<InstallProgressChangedEventArgs> Installing;
    EventHandler<InstallCompletedEventArgs> Installed;
}

I am contemplating whether to use "standard" Thread or use the BackgroundWorker class provided by .NET.

I've read that one should only use BackgroundWorker when it is expected that the object will be used in "Windows Forms" or UI. The class library I'm writing can be used on Windows Form, WPF Application and Console application.

Also, if I am to use the BackgroundWorker class, is it "proper" to not subclass from the BackgroundWorker class? I don't want to expose the class' members as I will be providing my own events (Installing and Installed).

If the BackgroundWorker class is not appropriate, what can you recommend and why?

Community
  • 1
  • 1
Ian
  • 5,625
  • 11
  • 57
  • 93

2 Answers2

4

There's no problems using the BW component outside of Windows forms UI applications. It's meant to be used where you just need to easily fire off a background thread and receive the callback when that work is complete.

If your only requirement is that you want to start up a thread to do work, and get results back, and you don't need the control you get by managing the thread yourself, I would recommend you use it.

I wouldn't subclass it though, unless you want your component to BE a background worker itself. That is, you're extending its behaviors. If you're simply using its behaviors there's no need to subclass, just use composition.

Andy
  • 8,432
  • 6
  • 38
  • 76
  • Well, there is a problem using BGW like that, it won't work the way it is described in the MSDN Library. There really isn't any point in using it, the events all run on tp threads. Might as well use the same thread. – Hans Passant Mar 29 '11 at 17:37
  • Care to elaborate on "it won't work the way it is describe in MSDN?" – Andy Mar 29 '11 at 20:13
1

I recommend to use the ThreadPool:

ThreadPool.QeueuUserWorkItem((_state)=>{ this.OnInstalling( ... ); });

EDIT:

The using the ThreadPool instead of BackgroundWorker is "slimmer". The BackGroundWorker must create "big" instance encapsulating all the "multithreading stuff", but BuckgroundWorker in internals use Thread and/or ThreadPool (I don't remember which one).

Using ThreadPool instead of Thread is better for performance because when the action (sent to the ThreadPool) is done, the thread is reused for another actions in future. But creation new thread is "expensive" operation, reusing the thread is "cheap".

EDIT 2:

If you want raise event in paralel thread often the "nice" way is using the extension methods for that:

class Product {
    public void Install() {
        this.OnInstalling( ... );
        ...
        this.OnInstalled( ... );
    }
    public void InstallAsync() {
        this.OnInstallingAsync( ... );
        ...
        this.OnInstalledAsync( ... );
    }

    protected virtual void OnInstalling( InstallProgressChangedEventArgs e ) {
        this.Installing.Raise(this, e);
    }
    protected virtual void OnInstallingAsync( InstallProgressChangedEventArgs e ) {
        this.Installing.RaiseAsync(this, e);
    }
    public event EventHandler<InstallProgressChangedEventArgs> Installing;

    protected virtual void OnInstalled( InstallCompletedEventArgs e ) {
        this.Installed.Raise(this, e);
    }
    protected virtual void OnInstalledAsync( InstallCompletedEventArgs e ) {
        this.Installed.RaiseAsync(this, e);
    }
    public event EventHandler<InstallCompletedEventArgs> Installed;
}
// ...
public static class EventExtensions {
    public static void Raise( this EventHandler handler, object sender, EventArgs e ) {
        if (null != handler) { handler(sender, e); }
    }

    public static void Raise<TEventArgs>( this EventHandler<TEventArgs> handler, object sender, TEventArgs e ) where TEventArgs : EventArgs {
        if (null != handler) { handler(sender, e); }
    }

    public static void RaiseAsync( this EventHandler handler, object sender, EventArgs e ) {
        if (null != handler) {
            ThreadPool.QeueuUserWorkItem((_state)=>{ handler(sender, e); });
        }
    }

    public static void RaiseAsync<TEventArgs>( this EventHandler<TEventArgs> handler, object sender, TEventArgs e ) where TEventArgs : EventArgs {
        if (null != handler) {
            ThreadPool.QeueuUserWorkItem((_state)=>{ handler(sender, e); });
        }
    }
}
TcKs
  • 25,849
  • 11
  • 66
  • 104
  • Thanks for the reply. Can you please provide why ThreadPool is recommended? – Ian Mar 29 '11 at 16:43
  • Thread Pool with some idle threads is great because you don't waste resources for creating a thread each time you do some work. – Lukasz Madon Mar 29 '11 at 16:45
  • The BackgroundWorker component also uses ThreadPool threads when it wants to do its work. – Andy Mar 29 '11 at 16:46
  • @Ian: As @lukas said - ThreadPool is better for performance. See me "edit" part. – TcKs Mar 29 '11 at 16:49
  • @Edit: So the choice between the two is performance? Are we not micro-optimizing this? I think that the BW code is more maintainable? – Ian Mar 29 '11 at 16:52
  • @Ian: I think the using BW for "only" raising events in paralel thread is hammer-for-fly. And you must write much more code for BW instead of calling one method. Of course - you can use BW for that, but it looks to me like using html-renderer for simple plain text. – TcKs Mar 29 '11 at 16:54
  • @Ian: I added some sample code to show how easy is raising events asynchronously. – TcKs Mar 29 '11 at 17:03
  • 1
    @TCKS: Thanks for such an elegant use Extensions, Threadpools and anonymous! I'll take a closer look at this tomorrow at the office. – Ian Mar 29 '11 at 17:23