【发布时间】:2023-04-04 07:32:01
【问题描述】:
我有一个稍微复杂的要求,即并行执行一些任务,并且必须等待其中一些任务完成才能继续。现在,我遇到了意外行为,当我有许多任务时,我想并行执行,但在 ContinueWith 处理程序中。我准备了一个小样本来说明这个问题:
var task1 = Task.Factory.StartNew(() =>
{
Console.WriteLine("11");
Thread.Sleep(1000);
Console.WriteLine("12");
}).ContinueWith(async t =>
{
Console.WriteLine("13");
var innerTasks = new List<Task>();
for (var i = 0; i < 10; i++)
{
var j = i;
innerTasks.Add(Task.Factory.StartNew(() =>
{
Console.WriteLine("1_" + j + "_1");
Thread.Sleep(500);
Console.WriteLine("1_" + j + "_2");
}));
}
await Task.WhenAll(innerTasks.ToArray());
//Task.WaitAll(innerTasks.ToArray());
Thread.Sleep(1000);
Console.WriteLine("14");
});
var task2 = Task.Factory.StartNew(() =>
{
Console.WriteLine("21");
Thread.Sleep(1000);
Console.WriteLine("22");
}).ContinueWith(t =>
{
Console.WriteLine("23");
Thread.Sleep(1000);
Console.WriteLine("24");
});
Console.WriteLine("1");
await Task.WhenAll(task1, task2);
Console.WriteLine("2");
基本模式是: - 任务 1 应与任务 2 并行执行。 - 一旦第 1 部分的第一部分完成,它应该同时做更多的事情。我想完成,一旦一切都完成了。
我希望得到以下结果:
1 <- Start
11 / 21 <- The initial task start
12 / 22 <- The initial task end
13 / 23 <- The continuation task start
Some combinations of "1_[0..9]_[1..2]" and 24 <- the "inner" tasks of task 1 + the continuation of task 2 end
14 <- The end of the task 1 continuation
2 <- The end
相反,await Task.WhenAll(innerTasks.ToArray()); 不会“阻止”继续任务完成。因此,内部任务在外部await Task.WhenAll(task1, task2); 完成后执行。结果是这样的:
1 <- Start
11 / 21 <- The initial task start
12 / 22 <- The initial task end
13 / 23 <- The continuation task start
Some combinations of "1_[0..9]_[1..2]" and 24 <- the "inner" tasks of task 1 + the continuation of task 2 end
2 <- The end
Some more combinations of "1_[0..9]_[1..2]" <- the "inner" tasks of task 1
14 <- The end of the task 1 continuation
如果我改为使用Task.WaitAll(innerTasks.ToArray()),一切似乎都按预期工作。当然,我不想使用 WaitAll,所以我不会阻塞任何线程。
我的问题是:
- 为什么会出现这种意外行为?
- 如何在不阻塞任何线程的情况下补救这种情况?
非常感谢您的任何指点!
【问题讨论】:
-
对问题 1 的回答是因为 ContinueWith 异步运行其内容,并且没有任何东西在等待它们;您正在等待 task1,其中 continueWith 不是其中的一部分。
-
看起来您只想将“continueWith”部分内联到 task1(而不是 continueWith)以实现所需的结果。这就是你所说的。在任务 1 中执行这些步骤,然后执行这些其他任务。这不需要 continueWith。
-
内联并不是一个真正的选择,因为第一个任务已经是异步的,需要在继续运行之前完成。当然,我可以使用包装任务,在其中,只需等待第一个操作,然后启动其他操作。但是,我将有两项任务,我认为一项就足够了。另一方面,
ContinueWith似乎就是通过返回Task<Task<T>>来做到这一点的,所以我想这无关紧要,额外的任务来自哪里。如果我错了,请纠正我。
标签: c# multithreading asynchronous async-await