我对 Task.Run 实现有疑问,我已经阅读了其他线程,只需要澄清我的用例。
这是我的代码概要:
public class MyClass
{
public async Task<object> Start(string arg1, string arg2)
{
object result;
If (someCondition)
result = await OtherClass.Task(arg1, arg2); // not worried about this
else
result = await Run(arg1, arg2); // here is my concern
// Cleanup
return result;
}
private async Task<object> Run(arg1, arg2)
{
// validate args (synchronous code block)
resultsBuilder = new();
await Dig(MyDir);
result = resultsBuilder.GetResults();
// cleanup logic
return result;
}
private async Task Dig(DirectoryInfo dir)
{
ForEach (file in Dir.GetFiles())
// unsure about this spot too - which one is better for this setup?
file.Move()
await Task.Run(() => file.Move());
ForEach (dir in Dir.GetDirectories())
await Dig(dir);
}
}
可以看出,它是有效的同步代码,在 Dig() 方法中使用了一些 ForEach 循环。当需要挖掘子目录时,Dig() 函数会再次调用,使其等待所有子目录完成后再退出。我相信,尽管这一切都在等待,但它仍然是有效的同步代码。
这将首先从 UI 线程启动,我不想挂起它。我的理解是,目前,每个“等待”都会返回到初始调用线程(可能是 UI 线程)。我说的对吗?
在 Run() 方法中使用 .ConfigureAwait(false) 就足够了吗?它也应该在递归 Dig() 方法中吗?或者我应该使用 Task.Run(() => Run(arg1, arg2));
或者,让递归方法同步且不返回,然后使用 Task.Run() 是否有意义? (并将 File.Move() 保留为同步,因为由于 Task.Run() 它已经在后台线程上运行) 示例:
await Task.Run(() => Dig(MyDir));
private void Dig(DirectoryInfo dir){ }
我相信,尽管这一切都在等待,但它仍然是有效的同步代码。
如果
Dig
使用同步Move
,那么是的,它将是完全同步的。
我的理解是,目前,每个“await”都会返回到初始调用线程(可能是 UI 线程)。我说的对吗?
关闭。我在我的博客上解释了它是如何工作的。当 await
与任务一起使用时,重要的部分是:
await
async
方法。当任务未完成时,await
SynchronizationContext.Current
或TaskScheduler.Current
,这个上下文用于任务完成后恢复执行。
SynchronizationContext
,因此
await
将在 UI 线程上恢复。线程池线程(和控制台应用程序)没有 SynchronizationContext
或 TaskScheduler
,因此 await
将在 任何可用的线程池线程上恢复。 在 Run() 方法中使用 .ConfigureAwait(false) 就足够了吗?它也应该在递归 Dig() 方法中吗?或者我应该使用 Task.Run(() => Run(arg1, arg2));
ConfigureAwait(false)
表示未捕获上下文(步骤 2),并且
async
方法会在任何可用的线程池线程上恢复执行。然而,只有当任务尚未完成时(步骤 1),这才会发挥作用。因此,如果您已经完成了任务,那么 ConfigureAwait(false)
不会 导致切换到线程池;它只是继续在同一个线程上同步执行。或者,让递归方法同步且不返回,然后使用 Task.Run() 是否有意义? (并将 File.Move() 保留为同步,因为由于 Task.Run(),它已经在后台线程上运行)
并保持递归方法同步。这正是我要做的:单个
Task.Run
异步递归方法并没有什么错误,但是您在这里真正要问的是更多关于并行性的问题,在这种情况下正确执行并行性很复杂。一方面是,在一般的递归树结构中,您通常希望限制并行性,因此最终会在代码中得到更多的生产者/消费者或数据流结构,而不是直接的递归。另一方面是,这涉及到磁盘,磁盘往往不能很好地并行化(特别是非 SSD,其中竞争搜索意味着并行性很容易比串行访问慢)。
因此,重申一下:如果足够有效的话,我会推荐使用简单的递归同步方法的Task.Run