如何使 ConcurrentExclusiveSchedulerPair 使用 async 和 await

How to make ConcurrentExclusiveSchedulerPair work with async and await

我有以下代码:

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 returns 完成任务时发生,并且从 ConcurrentExclusiveSchedulerPair 的角度来看它确实完成了执行。我也知道一些解决方法,但是有什么正确的方法可以 运行 这种模式与异步?

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

可用于控制 async/await 行为的抽象是 SynchronizationContext. It is quite similar to a TaskScheduler. So much actually that some people have been wondering why we need both: What is the conceptual difference between SynchronizationContext and TaskScheduler

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

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 是方法被分解的 "split" 点。 只有那些较小的任务才是TaskScheduler.

实际安排的任务

所以在你的例子中,你的代码开始了 10 ThreadTask 次调用,每个 运行 在并发调度程序上,并且每个都命中一个 await 点。然后代码在调度程序上调用 Complete,告诉它不再接受任何任务。然后当 await 完成时,他们将 async 方法继续(作为任务)安排到已经完成的任务调度程序。

因此,虽然从技术上讲 await 旨在与 TaskScheduler 一起使用,但实际上很少有人那样使用它。特别是,"concurrent" 和 "exclusive" 任务调度程序可能具有令人惊讶的语义,因为由于 await 而暂停的方法对于任务调度程序来说不算作 "running"。