【问题标题】:Simplifying anonymous methods for background worker简化后台工作人员的匿名方法
【发布时间】:2015-03-08 01:22:39
【问题描述】:

我正在尝试通过单击按钮使我的所有代码在后台工作人员中运行。所以我有以下代码模板。

BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += delegate
{
    //code
};
backgroundWorker.RunWorkerAsync();
while (backgroundWorker.IsBusy)
{
    Application.DoEvents();
} 

只是想知道是否有办法简化此代码,因此我没有为所有按钮有效地复制相同的代码块。

编辑: 我尝试运行的典型代码如下:

//Synchronous task
Wait(2000);
//Synchronous task
return taskResult ? "Success" : "Failure"

【问题讨论】:

  • 你对 async 和 await 有什么了解吗?您可以更轻松地将处理程序代码放入异步方法中。
  • 对它不是很熟悉,但我读过它,推荐用于 NET 4.5。只是还没有花时间去了解它。有什么好处吗?
  • 你通过在 doevents 上循环来破坏后台工作人员的目的。
  • 我的目标实际上只是保持 GUI 的功能。我正在执行的大多数任务需要 2-3 秒。所以它们足够长以至于它们使 GUI 冻结,但不会太长以至于我想让它们真正在后台运行。实现这一目标的正确方法是什么?
  • @Mayura Vivekanandra 如果您需要获得结果,我会让处理程序异步,首先调用它们而不等待,然后在完成其他工作后捕获结果。了解Stephen Cleary

标签: c# delegates backgroundworker anonymous-function


【解决方案1】:

如果没有更多上下文,就不可能提出非常具体的改进建议。就是说,您当然可以摆脱while 循环。只需使用:

BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += delegate
{
    //code
};
backgroundWorker.RunWorkerAsync();

请注意,如果您有想要在 BackgroundWorker 任务完成时执行的代码(可能解释了为什么您首先使用 while 循环),这样的事情将起作用(而不是使用 @ 987654325@你之前的循环):

BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += delegate
{
    //code
};
backgroundWorker.RunWorkerCompleted += (sender, e) =>
{
    // code to execute when background task is done
};
backgroundWorker.RunWorkerAsync();

在现代 C# 中,BackgroundWorker 几乎已过时。它现在提供的主要好处是方便的进度报告,实际上可以通过使用Progress<T> 轻松获得。

如果您不需要报告进度,将代码分解为使用async/await 很容易:

await Task.Run(() =>
{
    //code
});

// code to execute when background task is done

如果您确实需要报告进度,只是稍微困难一点:

Progress<int> progress = new Progress<int>();

progress.ProgressChanged += (sender, progressValue) =>
{
    // do something with "progressValue" here
};

await Task.Run(() =>
{
    //code

    // When reporting progress ("progressValue" is some hypothetical
    // variable containing the progress value to report...Progress<T>
    // is generic so you can customize to do whatever you want)
    progress.Report(progressValue);
});

// code to execute when background task is done

最后,在某些情况下,您可能需要任务返回一个值。在那种情况下,它看起来更像这样:

var result = await Task.Run(() =>
{
    //code

    // "someValue" would be a variable or expression having the value you
    // want to return. It can be of any type.
    return someValue;
});

// At this point in execution "result" now has the value returned by the
// background task. Note that in the above example, the method itself
// is anonymous and so you could just set a local variable at the end of the
// task; the value-returning syntax is more useful when you are calling an
// actual method that itself returns a value, and is especially useful when
// you are calling an `async` method that returns a value (i.e. you're not
// even using `Task.Run()` in the `await` statement.

// code to execute when background task is done

您可以根据需要混合搭配上述技术。

【讨论】:

  • 谢谢。真的无法理解我需要如何使用异步匿名调用我的代码。
  • 我仍然不明白的一件事是为什么你在'() =>'之前不需要异步。根据我有限的理解,我认为您必须使用 async 关键字才能允许使用 await 关键字。我说我可以使用 Task.Wait() 而不是 await 是否正确?还是不会达到相同的结果。
  • @MayuraVivekananda:“为什么在 '() =>' 之前不需要异步”——匿名方法(使用 lambda 语法声明)本身并不调用await,这就是为什么它不需要async。该匿名方法表示的整个任务通过将其包装在对Task.Run() 的调用中而实现异步,但该方法本身在Run() 方法创建的单个Task 中完整运行。
  • @MayuraVivekananda:“我可以使用Task.Wait() 代替等待吗?” ——不,那不会达到同样的结果。如果您要调用Wait(),那么代码将在那里阻塞当前线程,直到任务完成才继续在该线程中执行。但是await 恰恰相反;它向编译器表明当前方法实际上应该在启动异步操作之后返回,从而允许线程继续执行(即在方法的调用者中)。该方法的其余部分稍后在异步操作完成时执行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多