我希望这是有道理的 - 假设我有以下代码:
Task.Run(() =>
{
return Task.WhenAll
(
Task1,
Task2,
...
Taskn
)
.ContinueWith(tsks=>
{
TaskA (uses output from Tasks Task1 & Task2, say)
}
, ct)
.ContinueWith(res =>
{
TaskB (uses output from TaskA and Task3, say)
}
, ct);
});
所以,我希望所有我的第N个任务同时运行(因为我们没有相互依赖性),那么只有当他们全部完成后,继续依赖于它们的输出任务(我拿到这个,我可以使用tsks.Result
)。 但后来我想继续依靠的首要任务之一,也是结果的任务TaskA
。
我有点失去了如何正确地构建我的代码,所以我可以访问我的第一组立即着手之外的任务的结果ContinueWith
。
我唯一的想法是我的方法中的返回值分配给他们 - 是这样的:
... declare variables outside of Tasks ...
Task.Run(() =>
{
return Task.WhenAll
(
Task.Run(() => { var1 = Task1.Result; }, ct),
...
Task.Run(() => { varn = Taskn.Result; }, ct),
)
.ContinueWith(tsks=>
{
TaskA (uses output from Tasks var1 & varn, say)
}
, ct)
.ContinueWith(res =>
{
TaskB (uses output from TaskA and var3, say)
}
, ct);
});
不过,尽管这对我的作品,我毫不怀疑 ,是做错了。
什么是正确的方法是什么? 如果我有一个包含所有必需的变量,并通过在整个我的所有任务的状态对象? 有没有在总更好的办法?
请在此处原谅我的无知 - 我只是很新的并发编程。
由于Task1
, Task2
,..., TaskN
在范围上进行的呼叫WhenAll
,同时,由于时间ContinueWith
将控制传递给下一个任务所有早期任务,保证完成,它是安全的使用TaskX.Result
内代码执行的延续:
.ContinueWith(tsks=>
{
var resTask1 = Task1.Result;
...
}
, ct)
你肯定可以得到的结果不会阻塞,因为任务Task1
完成运行。
这里是一个办法与ConcurrentDictionary,这听起来像它可能适用于您的使用情况下做到这一点。 此外,由于你是新来的并发,就说明你Interlocked类以及:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Executing...");
var numOfTasks = 50;
var tasks = new List<Task>();
for (int i = 0; i < numOfTasks; i++)
{
var iTask = Task.Run(() =>
{
var counter = Interlocked.Increment(ref _Counter);
Console.WriteLine(counter);
if (counter == numOfTasks - 1)
{
Console.WriteLine("Waiting {0} ms", 5000);
Task.Delay(5000).Wait(); // to simulate a longish running task
}
_State.AddOrUpdate(counter, "Updated Yo!", (k, v) =>
{
throw new InvalidOperationException("This shouldn't occure more than once.");
});
});
tasks.Add(iTask);
}
Task.WhenAll(tasks)
.ContinueWith(t =>
{
var longishState = _State[numOfTasks - 1];
Console.WriteLine(longishState);
Console.WriteLine("Complete. longishState: " + longishState);
});
Console.ReadKey();
}
static int _Counter = -1;
static ConcurrentDictionary<int, string> _State = new ConcurrentDictionary<int, string>();
}
你得到类似的输出连接到这个(虽然它的等待线不会永远延续前的最后一次):
解决这个优雅的方式是使用屏障类 。
像这样:
var nrOfTasks = ... ;
ConcurrentDictionary<int, ResultType> Results = new ConcurrentDictionary<int, ResultType>();
var barrier = new Barrier(nrOfTasks, (b) =>
{
// here goes the work of TaskA
// and immediatley
// here goes the work of TaskB, having the results of TaskA and any other task you might need
});
Task.Run(() => { Results[1] = Task1.Result; barrier.SignalAndWait(); }, ct),
...
Task.Run(() => { Results[nrOfTasks] = Taskn.Result; barrier.SignalAndWait(); }, ct
文章来源: Correct way to link Tasks together when return values are needed at different times