【问题标题】:How to make ConcurrentExclusiveSchedulerPair work with async and await如何使 ConcurrentExclusiveSchedulerPair 与 async 和 await 一起工作
【发布时间】:2019-11-23 21:00:45
【问题描述】:

我有以下代码:

static async Task Main()
{
    ConcurrentExclusiveSchedulerPair concurrentExclusiveSchedulerPair = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 4);
    var factory = new TaskFactory(concurrentExclusiveSchedulerPair.ConcurrentScheduler);
    for (int i = 0; i < 10; i++)
    {
        factory.StartNew(ThreadTask);
    }

    concurrentExclusiveSchedulerPair.Complete();

    await concurrentExclusiveSchedulerPair.Completion;
    Console.WriteLine("Completed");
}

private static async Task ThreadTask()
{
    var random = new Random();
    await Task.Delay(random.Next(100, 200));
    Console.WriteLine($"Finished {Thread.CurrentThread.ManagedThreadId}");
}

并且程序在任务完成之前完成执行。我理解为什么它会在ThreadTask 返回完成的任务时发生,并且从ConcurrentExclusiveSchedulerPair 的角度来看它确实完成了执行。我也知道一些解决方法,但是有没有正确的方法来异步运行这个模式?

【问题讨论】:

    标签: c# multithreading task task-parallel-library


    【解决方案1】:

    TaskScheduler 的概念是在 async/await 出现之前设计的,但最终与它不兼容。您可以在此处查看演示此不兼容性的实验:How to run a Task on a custom TaskScheduler using await?

    可用于控制 async/await 行为的抽象是 SynchronizationContext。它与TaskScheduler 非常相似。实际上,有些人一直想知道为什么我们需要两者:What is the conceptual difference between SynchronizationContext and TaskScheduler

    如果您对 SingleThreadSynchronizationContext 之类的东西感兴趣,可以在此处找到实现:Await, SynchronizationContext, and Console Apps

    【讨论】:

      【解决方案2】:

      TaskSchedulerasync/await 兼容,但某些行为可能令人惊讶。

      await 捕获其上下文时,it captures the current SynchronizationContext unless it is null, in which case it captures the current TaskScheduler。因此,ThreadTask 将使用并发调度程序开始执行,在其 await 之后它将在同一个并发调度程序上恢复。

      但是,语义可能令人惊讶,因为async 与任务调度程序一起工作的方式是将async 方法拆分为多个 任务。每个await 都是方法被分解的“分裂”点。而且只有那些较小的任务才是真正TaskScheduler安排的。

      因此,在您的情况下,您的代码开始 10 个ThreadTask 调用,每个调用都在并发调度程序上运行,并且每个调用都达到await 点。然后代码在调度程序上调用Complete,告诉它不再接受任何任务。然后当awaits 完成时,他们将async 方法继续(作为任务)调度到已经完成的任务调度程序。

      因此,虽然从技术上讲 await 旨在与 TaskScheduler 一起使用,但实际上很少有人以这种方式使用它。特别是,“并发”和“独占”任务调度程序可能具有令人惊讶的语义,因为由于 await 而挂起的方法对于任务调度程序来说不算“正在运行”。

      【讨论】:

        猜你喜欢
        • 2017-10-24
        • 2019-09-29
        • 1970-01-01
        • 2021-06-30
        • 2018-09-03
        • 1970-01-01
        • 1970-01-01
        • 2014-03-13
        • 2017-07-02
        相关资源
        最近更新 更多