我有以下代码:
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
的角度来看它确实完成了执行的原因。我也知道一些解决方法,但是有没有正确的方法来异步运行此模式?
TaskScheduler
的概念是在异步/等待出现之前设计的,最终与之不兼容。您可以在此处看到一个证明这种不兼容性的实验:How to run a Task on a custom TaskScheduler using await?
可用于控制异步/等待行为的抽象是SynchronizationContext
。它与SynchronizationContext
非常相似。实际上很多人一直在想为什么我们都需要:TaskScheduler
。
如果您对What is the conceptual difference between SynchronizationContext and TaskScheduler之类的东西感兴趣,可以在此处找到实现:SingleThreadSynchronizationContext
[Await, SynchronizationContext, and Console Apps
与Await, SynchronizationContext, and Console Apps
/ TaskScheduler
兼容,但是某些行为可能令人惊讶。
[async
捕获其上下文时,为await
。因此,await
将开始使用并发调度程序执行,并且在其it captures the current SynchronizationContext
unless it is null
, in which case it captures the current TaskScheduler
之后将在同一并发调度程序上继续执行。
但是,语义可能令人惊讶,因为SynchronizationContext
与任务计划程序一起工作的方式是null
方法被拆分为multiple个任务。每个TaskScheduler
是一个“拆分”点,该方法被分解。而[[仅那些较小的任务实际上是ThreadTask
的计划的。
await
调用,每个调用均在并发调度程序上运行,并且每个调用都达到async
点。然后代码在调度程序上调用async
,告诉它不接受更多任务。然后,当await
完成时,他们将TaskScheduler
方法继续(作为一项任务)安排到该任务计划程序,该任务计划程序已经完成。因此,虽然从技术上讲ThreadTask
设计为可与await
一起使用,但实际上很少有人以这种方式使用它。特别是,“并发的”和“排他的”任务计划程序可能具有令人惊讶的语义,因为由于Complete
而暂停的方法不算作任务计划程序的“运行”。