我的递归异步函数会在 UI 线程上运行吗?

问题描述 投票:0回答:1

我对 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){ } 
c# async-await task
1个回答
0
投票

我相信,尽管这一切都在等待,但它仍然是有效的同步代码。

如果

Dig
使用同步
Move
,那么是的,它将是完全同步的。

我的理解是,目前,每个“await”都会返回到初始调用线程(可能是 UI 线程)。我说的对吗?

关闭。我在我的博客上解释了它是如何工作的。当 await 与任务一起使用时,重要的部分是:


    await
  1. 首先检查任务以确定它是否已完成。如果是,则继续同步执行
    async
    方法。
    当任务未完成时,
  2. await
  3. 会捕获当前的“上下文”,即
    SynchronizationContext.Current
    TaskScheduler.Current
    ,这个上下文用于任务完成后恢复执行。
    
    
  4. UI 线程有一个
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

© www.soinside.com 2019 - 2024. All rights reserved.