[英]How to prioritize tasks generated by await when using async / await
我有大量数据要处理。 当前代码可以简化如下:
public void ProcessData(string data)
{
string resultOfA = doCpuBoundWorkA(data);
string resultOfS1 = sendToServiceS1(resultOfA);
string resultOfB = doCpuBoundWorkB(resultOfS1);
string resultOfS2 = sendToServiceS2(resultOfB);
string resultOfC = doCpuBoundWorkC(resultOfS2);
}
使用Parallel.ForEach
调用 ProcessData。 至少从两个角度来看,这种实施方式并不是最优的。 首先,对服务的调用是阻塞的,所以我们在等待调用返回时阻塞了线程。 其次, Parallel.ForEach
创建计划在线程池上执行的任务。 线程池每 500 毫秒创建一次额外的线程(如果我没记错的话)并且因为“ProcessData”需要超过 500 毫秒才能完成,随着时间的推移,我们最终会得到数百个线程,这些线程大部分时间都在等待服务卷土重来。
我对“改进”的幼稚想法是:
public async Task ProcessData(string data)
{
string resultOfA = doCpuBoundWorkA(data);
string resultOfS1 = await sendToServiceS1Async(resultOfA);
string resultOfB = doCpuBoundWorkB(resultOfS1);
string resultOfS2 = await sendToServiceS2Async(resultOfB);
string resultOfC = doCpuBoundWorkC(resultOfS2);
}
我是 async/await 的新手,所以我对它实际发生的事情的理解可能是完全错误的。
使用 async/await 关键字,编译器将 ProcessData 的代码分解为多个任务。
结果,我们有了三个“工作处理单元”,而不是一个单一的“工作处理单元”,其中每个部分都根据其在调度程序队列中的位置安排执行。
问题是,当 Task-B(对于第一项工作)被放入调度程序的队列时,我可能有数百个 Task-A,由Parallel.ForEach
放在那里,到时候 Task-C(对于first piece of work)放在调度器的队列中情况会更糟。
我希望数据尽可能快地通过,所以我需要能够优先考虑任务 C,而不是任务 B,而不是任务 A。 实现这一目标的最佳方式是什么?
INotifyCompletion
, SynchronizationContext
浮现在脑海中,但它似乎是 async/await 的“暗角”。 ParallelExtensionsExtras 具有带有优先队列的ReprioritizableTaskScheduler
和QueuedTaskScheduler
,但我如何告诉 async/await 使用所需的调度程序?
John Skeet 在他的博客中谈到了这个问题: https://codeblog.jonskeet.uk/2010/11/02/configuring-waiting/
节流可能是比确定优先级更容易的方法。
我认为您的问题最好由TPL 数据流库解决。 它结合了并行和async
技术。
您可以创建“块”并将它们“链接”在一起以形成“网格”(在您的情况下,网格是管道)。 TransformBlock
可用于同步和异步操作,还支持内置的并行性和节流。
或者,您可以使用SemaphoreSlim
将异步节流应用到ProcessData
方法(在方法开始时调用WaitAsync
,在方法结束时调用Release
)。 但是请考虑 TPL Dataflow; 我发现如果人们正在做这么复杂的事情,那么他们通常会发现他们也可以在他们应用程序的其他部分使用 TPL 数据流。
你的问题:
线程池每 500 毫秒创建一次额外的线程(如果我没记错的话)并且因为“ProcessData”需要超过 500 毫秒才能完成,随着时间的推移,我们最终会得到数百个线程,这些线程大部分时间都在等待服务回来。
可以通过等待 ProcessData 来“修复”,并且只在完成后产生新的。 (或做类似 Task.WhenAll(...Task.Delay(500)..., ...ProcessData()) 之类的事情。
ProcessData 中的所有调用都依赖于数据,
string resultOfA = doCpuBoundWorkA(data); string resultOfS1 = await sendToServiceS1Async(resultOfA); string resultOfB = doCpuBoundWorkB(resultOfS1); string resultOfS2 = await sendToServiceS2Async(resultOfB); string resultOfC = doCpuBoundWorkC(resultOfS2);
IIRC,await 仅将执行传递给方法“ProcessData”之外。 所以只能让其他async方法运行,但是ProcessData内部的调用因为数据依赖还是背靠背的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.