【问题标题】:Continuation on multiple concurrent threads in async method after async IO API call异步 IO API 调用后异步方法中多个并发线程的继续
【发布时间】:2021-07-10 13:33:30
【问题描述】:

如果我有这个

static void Main(string[] args)
{
    var tasks = new List<Task>();
    for (int i = 0; i < 10; i++)
    {
        tasks.Add(AsyncWithBlockingIO(i));
    }

    Task.WaitAll(tasks.ToArray());
}

private static async Task AsyncWithBlockingIO(int fileNum)
{
    var result = await File.ReadAllTextAsync($"File{fileNum}").ConfigureAwait(false);
    //will the below run concurrently on multiple threads?
    CpuNoIOWork(result);
}

CpuNoIOWork() 会在 IO 调用完成时同时在多个线程上运行(使用线程池)还是一次只使用 1 个线程?

【问题讨论】:

  • 你可以在CpuNoIOWork()后面加上Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);来证明continuation确实都是并发运行的。
  • 您可能想看看这个:Why File.ReadAllLinesAsync() blocks the UI thread? 并非所有的异步广告方法都是真正异步的。
  • @TheodorZoulias,谢谢!没有在实际代码中使用文件 API,但了解一下很有用。

标签: c# .net multithreading async-await


【解决方案1】:

当 IO 调用完成时,CpuNoIOWork() 会在多个线程上并发运行(使用线程池)还是一次只使用 1 个线程?

它将在线程池线程上并发运行。

但是,根据您awaiting 的内部结构,它可能在 I/O 线程池线程上继续,这是不可取的。如果您有 CPU 密集型工作,我建议将其包装在 Task.Run 中,这将确保工作将在工作线程池线程上运行:

var result = await File.ReadAllTextAsync($"File{fileNum}").ConfigureAwait(false);
await Task.Run(() => CpuNoIOWork(result));

【讨论】:

  • 在这种特定情况下,await Task.Run 会强制延续在线程池线程上运行,而不是 await Task.Run
  • @JohanP:是的,但我更喜欢Task.Run,这样代码更明确。
  • @JohanP Task.Yield 是不存在的 API TaskScheduler.SwitchTo 的穷人替代品。 Task.Yield 的行为取决于环境 SynchronizationContext.Current(或环境 TaskScheduler.Current,如果上下文为空),因此切换到 ThreadPool 不是可靠(平台无关)方式,如果这是您的意图.
  • @TheodorZoulias 您是否会在 .net 核心中说在此特定实例中访问线程池线程是一种可靠的方式?
  • @JohanP 不,我不会。恕我直言,这不是工作的正确工具。如果出于某种原因我真的想避免使用Task.Run,我会使用像noseratio 的TaskSchedulerAwaiter 这样的自定义等待器,或者它的简化版本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多