在Windows.Forms中执行后台任务的最简单方法是什么?

时间:2009-10-06 10:46:44

标签: .net winforms multithreading design-patterns

后台任务涉及网络I / O,磁盘I / O或其他可能会或可能不会通过网络发生的长时间运行的任务。它经常与更新GUI的代码混合,后者需要在另一个线程GUI线程上运行。

简单的意思是,在打开Form.cs文件时,源代码比以前更容易或更容易阅读。实际上,源代码的流程仍然必须按照执行代码的顺序顺序读取,而不管它在哪个线程上执行。所有支撑织物必须可以重复使用并隐藏在某个地方,而不是包含在每个表格中。

谷歌搜索MSDN:发现微软正式批准的解决方案是System.ComponentModel.BackgroundWorker,它在第二点上落下(非常!)。

(在System.Windows.Threading.Dispatcher中还有官方批准的Silverlight / XAML / 3.5解决方案模型。)

8 个答案:

答案 0 :(得分:3)

如果你真的不喜欢BackgroundWorker,你可以为后台操作创建自己的基类,就像我here一样。

答案 1 :(得分:2)

您仍然可以使用BackgroundWorker。它不需要作为表单上的组件存在。您可以轻松地将其包装到一个类中,然后可以在每个表单中重复使用。

但是,这与在需要时简单地为后台任务设置工作程序没什么不同。

答案 2 :(得分:2)

使用BackgroundWorker代替

答案 3 :(得分:1)

你能解释为什么你说BackgroundWorker不合适吗?

在大多数情况下,它需要2-3行额外的代码。

答案 4 :(得分:1)

这是我迄今为止提出的最简单的想法。它可能完全不是犹太人,我的编码非常接近零.Windows.Forms应用程序。

它涉及一个帮助器,它有两个主要方法,Background()和Foreground()。这些中的任何一个都采用以lambda表达式形式指定的委托。 Background()在后台线程上启动任何给定的委托并立即返回。 Foreground()使用Form.BeginInvoke()将任何给定的委托“返回”发送到GUI线程并立即返回。

以下是如何使用此设计模式的代码示例,前提是已经实现了帮助程序。

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

这使代码整齐顺序,在一个好的位置提供共享变量,并允许跨越两个线程的循环。

澄清助手的作用,

  • 构造函数启动一个等待队列的后台线程。
  • Background()和Foreground()都会立即返回。
  • Background()将代码排入使用内部队列在后台线程中运行。
  • Foreground()执行相同操作,在首先创建帮助程序的GUI线程上使用BeginInvoke。

编辑:实施:
http://code.google.com/p/backgrounder/

答案 5 :(得分:0)

例如,关于磁盘I / O的问题,我最近编写了一个异步文件扫描程序类(或多年来第100次)。它可以像你想要的那样重复使用,只需实例化一个新实例并挂钩事件即可。

我将功能封装在实现跨线程安全的类中。该类触发事件以通知调用者更新以及何时完成。

你说“源代码仍然必须按顺序读取代码的执行顺序”,但要意识到线程彼此并行运行。这就是分离代码结构的好处。

这表明将可重用代码隔离到一个单独的类......

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

答案 6 :(得分:0)

在Windows窗体中执行后台任务并在完成事件时引发事件的替代方法是使用AsyncOperationManagerAsyncOperation类。要注意的是,必须在UI线程中创建AsyncOperation才能正常工作。否则,UI代码必须检查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);
    } 
  }
}

以下是代码的使用方法:

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

答案 7 :(得分:0)

正如有说服力here所说,微软已经看到了BackgroundWorker中的缺陷,你可以使用Task.Run和async关键字获得更好,更清晰的代码。