Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 在Windows.Forms中执行后台任务的最简单方法是什么?_.net_Winforms_Multithreading_Design Patterns - Fatal编程技术网

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

.net 在Windows.Forms中执行后台任务的最简单方法是什么?,.net,winforms,multithreading,design-patterns,.net,Winforms,Multithreading,Design Patterns,后台任务是指涉及网络I/O、磁盘I/O或其他可能在网络上发生或不发生的长时间运行任务的任务。它通常会与更新GUI的代码混合在一起,而GUI需要在另一个线程(GUI线程)上运行 简单的意思是,打开Form.cs文件时,源代码与以前一样容易阅读。实际上,源代码流仍然必须按照代码的执行顺序顺序读取,而不管它在哪个线程上执行。所有支撑结构必须可重复使用并隐藏在某处,而不是包含在每个表单中 谷歌搜索MSDN:发现微软官方认可的解决方案是System.ComponentModel.BackgroundWor

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

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

谷歌搜索MSDN:发现微软官方认可的解决方案是System.ComponentModel.BackgroundWorker,它(非常!)缺少第二点


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

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


然而,这与在需要时简单地为后台任务设置工作人员几乎没有什么不同。

用于此

您能解释一下为什么您说
后台工作人员
不足吗


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

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

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

它涉及一个助手,它有两个主要方法,Background()和Foreground()。其中任何一个都接受以lambda表达式形式指定的委托。Background()在后台线程上启动任何给定的委托,并立即返回。前台()使用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);
            }
        });
    }
}
公共类表单1:表单{
受保护的ProgressBar progressBar1;
保护按钮1;
受保护的BackgroundHelper=新的BackgroundHelper();
公共无效按钮1\u单击(…){
//在后台执行代码。
helper.Background(()=>{
对于(int i=0;i{
progressBar1.值=j;
});
//模拟执行I/O或其他操作。
睡眠(25);
}
});
}
}
这样可以保持代码整齐有序,在适当的位置提供共享变量,并允许跨两个线程的循环

为了澄清助手的工作

  • 构造函数启动一个等待队列的后台线程
  • 后台()和前台()都会立即返回
  • Background()使用内部队列将要在后台线程中运行的代码排队
  • 前台()也会这样做,首先创建帮助程序的GUI线程上会出现BeginInvoke
编辑:实现:

例如,根据您提出的磁盘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

在Windows窗体中执行后台任务并在完成后引发事件的另一种方法是使用和类。这里需要注意的是,必须在UI线程中创建AsyncOperation才能正常工作。否则,UI代码必须检查invokererequired

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

正如非常有说服力的争论所指出的,Microsoft已经看到了BackgroundWorker中的缺陷,使用Task可以获得更好、更清晰的代码。使用async关键字运行。

传统上,编写线程化代码一开始就是一项复杂的任务。就我个人而言,与早期.Net时代相比,后台工作者的方式非常简单,在早期.Net时代,您必须自定义滚动跨线程操作委托和回调。你能解释一下为什么它没有达到第二点吗?肯定比早期的dotnet更简单,同意。我在一个团队中工作,我们需要有一个标准的设计模式来完成这项工作,我们需要有非常可读的代码来实现协作。这是否足够详细,或者?你因为可读性、可读性和使用它所需的工作量而对Bgw打折。请记住,工作量是500倍,因为这是一个团队,有几个大型软件项目,这需要一次又一次地完成,每一个都需要一次……是的(希望如此):这不仅仅是需要多少额外的代码行的问题,但是BackgroundWorker的另一个问题是,它使原始的单线程代码无法读取。BackgroundWorker强制您将逻辑和一致的代码划分为一组事件处理程序和不同的方法。BG worker继承自组件库,因为它知道如何与其所有者控件同步,以进行跨线程安全调用。这就是为什么它“强制”您使用事件处理程序方法。如果您不需要线程安全,您可以始终使用threading.threadpool实现自己的线程类