如果您需要复制您原来的基于Thread 的行为,您可以使用Task.Factory.StartNew(... , TaskCreationOptions.LongRunning) 来安排您的工作,然后阻塞直到通过Task.WaitAll 完成工作任务。我不推荐这种方法,但就行为而言,这将非常接近您的代码之前的工作方式。
关于为什么在您的场景中可能无法获得预期性能的更深入分析如下:
解释,第 1 部分(async 并不意味着“在不同的线程上”)
标有async 关键字的方法不会神奇地异步运行。它们只是能够将等待操作(可能会或可能不会异步运行)组合成一个更大的单元(通常是Task 或Task<T>)。
如果您的insert 方法是async,它仍然可能至少同步执行一些 工作。在第一个 await 语句之前的 all 代码肯定会出现这种情况。这项工作将在“主”线程(调用insert的线程)上执行-这将是您的瓶颈或至少是瓶颈的一部分,因为您调用@时该部分代码的并行度将为1 987654332@在一个紧密的循环中,不管你是否await产生的任务。
为了说明上述观点,请考虑以下示例:
void Test()
{
Debug.Print($"Kicking off async chain (thread {Thread.CurrentThread.ManagedThreadId}) - this is the main thread");
OuterTask().Wait(); // Do not block on Tasks - educational purposes only.
}
async Task OuterTask()
{
Debug.Print($"OuterTask before await (thread {Thread.CurrentThread.ManagedThreadId})");
await InnerTask().ConfigureAwait(false);
Debug.Print($"OuterTask after await (thread {Thread.CurrentThread.ManagedThreadId})");
}
async Task InnerTask()
{
Debug.Print($"InnerTask before await (thread {Thread.CurrentThread.ManagedThreadId})");
await Task.Delay(10).ConfigureAwait(false);
Debug.Print($"InnerTask after await (thread {Thread.CurrentThread.ManagedThreadId}) - we are now on the thread pool");
}
这会产生以下输出:
启动异步链(线程 6) - 这是主线程
等待之前的 OuterTask(线程 6)
等待之前的 InnerTask(线程 6)
等待后的 InnerTask(线程 8)——我们现在在线程池中
等待后的 OuterTask(线程 8)
请注意,Task1 中第一个 await 之前的代码甚至 Task2 仍然在“主”线程上执行。我们的链实际上是在启动外部任务的同一线程上同步执行的,直到我们 await 第一个真正的异步操作(在本例中为 Task.Delay)。
另外
如果您在SynchronizationContext.Current 不为空的环境中运行(即Windows 窗体、WPF),并且您没有在insert 方法中等待的任务上使用ConfigureAwait(false),则由异步计划的继续状态机之后第一个await 语句也可能在“主”线程上执行 - 尽管在某些环境(即 ASP.NET)中不能保证这一点。
解释,第 2 部分(在线程池上执行 Tasks)
如果作为insert 方法的一部分,您选择手动启动任何Tasks,那么您很可能使用Task.Run 或任何其他 启动未指定TaskCreationOptions.LongRunning 的新任务的方法。一旦线程池饱和,任何新启动的任务都会排队,从而降低并行系统的吞吐量。
证明:
IEnumerable<Task> tasks = Enumerable
.Range(0, 200)
.Select(_ => Task.Run(() => Thread.Sleep(100))); // Using Thread.Sleep to simulate blocking calls.
await Task.WhenAll(tasks); // Completes in 2+ seconds.
现在TaskCreationOptions.LongRunning:
IEnumerable<Task> tasks = Enumerable
.Range(0, 200)
.Select(_ => Task.Factory.StartNew(
() => Thread.Sleep(100), TaskCreationOptions.LongRunning
));
await Task.WhenAll(tasks); // Completes in under 130 milliseconds.
生成 200 个线程通常不是一个好主意(这不会很好地扩展),但如果阻塞调用的大规模并行化是绝对要求,上面的 sn-p 向您展示了一种方法用 TPL 来做。