【发布时间】:2020-12-25 11:09:59
【问题描述】:
关于异步操作,我知道如果您将代码暴露在库中以供其他地方使用,那么在您的实现中使用 Task.Run 是一种不好的做法,因为您应该将此类决定留给库的调用者。该信息可以在此处的“重复”问题中找到。 但这不是我要问的。我正在寻求澄清我们自己的代码,该代码位于 WPF 桌面应用程序中,永远不会作为库公开供其他人使用.
此外,如果这有什么不同的话,我们的任务需要 1-2 分钟的密集 CPU 处理。
我的问题是在我们的特殊情况下,使用后台线程使我们的非异步函数异步是否有意义,或者我们只是让我们的函数异步,然后用 await 调用它?
此外,长时间运行的函数本身不执行任何异步/等待操作。这纯粹是一个线性的、CPU 密集型任务,我不在乎它在哪里运行,只关心它的返回值(和进度)会报告回主 UI。
这就是我要澄清的问题。
如果重要的话,我直接在 WPF 桌面应用程序中使用带有 C#9 的 .NET 5.0(即,此代码不在要与他人共享的库中)。
这里有一些(为了清楚起见已更新!)示例代码说明了这一点...
public static void Main() {
// Local function to start the import
// Note: I read I should be using `async void` and not 'async Task' here since
// this is a top-level async statement and I understand you need this for proper
// exception handling. Is that correct?
async void startImport(){
Debug.WriteLine($"Import starting... IsBackground: " +
$"{Thread.CurrentThread.IsBackground}");
var progress = new Progress<string>(message =>
Debug.WriteLine($"Progress Update: {message}"));
// Toggle the comments on these lines and the line defining
// CPUIntenseSequentialImport to try it with and without Task.Run()
var result = await Task.Run(() => CPUIntenseSequentialImport(progress));
//var result = await CPUIntenseSequentialImport(progress);
Debug.WriteLine($"Finished! The result is {result} - IsBackground: " +
$"{Thread.CurrentThread.IsBackground}");
}
startImport();
Debug.WriteLine($"Import started... IsBackground: " +
$"{Thread.CurrentThread.IsBackground}");
}
// Can you just mark this as async, then use await on it and not need Task.Run(),
// or because it's a long-running task, should you NOT mark it with async, and
// instead use Task.Start() so it gets its own thread from the pool?
static public int CPUIntenseSequentialImport(IProgress<string> progress) {
//static async public Task<int> CPUIntenseSequentialImport(IProgress<string> progress) {
Thread.Sleep(1000);
progress.Report($"First part of import done - IsBackground: " +
$"{Thread.CurrentThread.IsBackground}");
Thread.Sleep(1000);
progress.Report($"Next part of import done - IsBackground: " +
$"{Thread.CurrentThread.IsBackground}");
Thread.Sleep(1000);
progress.Report($"Final part of import done - IsBackground: " +
$"{Thread.CurrentThread.IsBackground}");
return 44;
}
按原样运行时,您会得到此信息(请注意,更新不是来自后台线程的报告)...
Import starting... IsBackground: False
Import started... IsBackground: False
Progress Update: First part of import done - IsBackground: False
Progress Update: Next part of import done - IsBackground: False
Progress Update: Final part of import done - IsBackground: False
Finished! The result is 44 - IsBackground: False
...但是当您交换注释/未注释的行时,您会得到这个(注意更新是从后台线程报告)...
Import starting... IsBackground: False
Import started... IsBackground: False
Progress Update: First part of import done - IsBackground: True
Progress Update: Next part of import done - IsBackground: True
Progress Update: Final part of import done - IsBackground: True
Finished! The result is 44 - IsBackground: False
【问题讨论】:
-
尝试等待Task.Delay(1000).ConfigureAwait(false);
-
Task.Run,默认情况下,将安排由线程池线程完成的工作 - 这显然不是总是需要的。必要时使用Task.Run- 推荐阅读blog.stephencleary.com/2013/11/… -
“我们的任务需要 1-2 分钟的密集处理” await Task.Delay(1000)模拟I/O-bound工作,
Thread.Sleep(1000)模拟CPU密集型工作。您所说的“密集处理”可能属于第二类,但最好澄清一下。您还应该澄清您想到的应用程序类型。对 GUI 应用程序有意义的东西可能对 ASP.NET 应用程序没有意义。 -
好点!是的,我们的长期运行任务不 执行任何内部异步/等待,所以你是对的……我使用 await
Task.Delay()令人困惑。我更新了代码和问题,以更好地说明我在追求/询问的内容。 -
没有太多要补充的,因为斯蒂芬已经为您的问题提供了所有正确的答案。但您可能仍想阅读my arguments,了解为什么在 WPF 应用程序的事件处理程序中使用
Task.Run是个好主意。
标签: c# asynchronous async-await task c#-9.0