当我需要一些并行处理时,我通常这样做:
static void Main(string[] args)
{
var tasks = new List<Task>();
var toProcess = new List<string>{"dog", "cat", "whale", "etc"};
toProcess.ForEach(s => tasks.Add(CanRunAsync(s)));
Task.WaitAll(tasks.ToArray());
}
private static async Task CanRunAsync(string item)
{
// simulate some work
await Task.Delay(10000);
}
我遇到过这样的情况,即它不能并行处理这些项,必须使用Task.Run
来强制它在不同的线程上运行。
我错过了什么?
2条答案
按热度按时间5gfr0r5j1#
Task
意味着"需要做的事情,可能已经完成,可能正在并行线程上执行,或者可能依赖于进程外数据(套接字等),或者可能只是......连接到某个显示"已完成"的开关"-它与线程处理的关系 * 非常小 *,除了:如果您调度了一个 * continuation *(akaawait
),那么 * 不知何故 * 它将需要返回到一个线程上才能触发,但这是如何发生的以及这意味着什么取决于创建和拥有该任务的代码。注意:并行性 * 可以用多个任务来表示 *(如果您选择这样做),但多个任务并不意味着并行性。
在您的情况下:这完全取决于
CanRun
的功能或特性--而 * 我们并不知道 *。它也可能被称为CanRunAsync
。1qczuiv02#
我遇到过这样的情况,即它不能并行处理这些项,必须使用
Task.Run
来强制它在不同的线程上运行。这些情况很可能与具有异步契约的方法有关,但它们的实现是同步的。例如:
任何调用该方法的线程都将被阻塞10秒,然后它将被交回一个已经完成的任务。尽管
NotAsync
方法的契约是异步的(它有一个可等待的返回类型),但它的实际实现是同步的,因为它在调用期间完成了所有工作。因此,当您试图通过调用该方法创建多个任务时:...当前线程将被阻塞10秒 * 任务数。创建这些任务时,它们都已完成,因此等待它们完成将导致零等待:
通过将
NotAsync
Package 在Task.Run
中,可以确保当前线程不会被阻塞,因为NotAsync
将在ThreadPool
上调用。Task.Run
立即返回一个Task
,保证零阻塞。应该注意的是,编写具有同步实现的异步方法违反了Microsoft的指导原则:
但有时候微软也会违反这条准则。因为违反这条准则比违反not exposing asynchronous wrappers for synchronous methods准则要好。换句话说,为了给人一种异步的印象,暴露内部调用
Task.Run
的API比阻塞当前线程更罪恶。