我希望这是有道理的 - 假设我有以下代码:
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);
});
但是,即使这对我有用,我也毫无疑问这样做错了。
正确的方法是什么?我应该有一个包含所有必要变量的状态对象,并在我的所有任务中传递它吗?总共有更好的方法吗?
请原谅我的无知 - 我对并发编程非常陌生。
答案 0 :(得分:2)
由于Task1
,Task2
,...,TaskN
适用于WhenAll
的调用,并且因为ContinueWith
通过控制的时间对于您的下一个任务,所有早期任务都可以保证完成,在实现continuation的代码中使用TaskX.Result
是安全的:
.ContinueWith(tsks=>
{
var resTask1 = Task1.Result;
...
}
, ct)
您可以保证在不阻止的情况下获得结果,因为任务Task1
已经完成运行。
答案 1 :(得分:0)
这是一种使用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>();
}
答案 2 :(得分:0)
解决此问题的一种优雅方法是使用Barrier class。
像这样:
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