C# 在不同时间需要返回值时将任务链接在一起的正确方法
我希望这是有意义的——假设我有以下代码:C# 在不同时间需要返回值时将任务链接在一起的正确方法,c#,concurrency,task-parallel-library,task,C#,Concurrency,Task Parallel Library,Task,我希望这是有意义的——假设我有以下代码: Task.Run(() => { return Task.WhenAll ( Task1, Task2, ... Taskn ) .ContinueWith(tsks=> { TaskA (uses output from Tasks T
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
<代码>任务n在调用whalll
的范围内,并且由于当ContinueWith
将控制权传递给下一个任务时,所有先前的任务都保证完成,因此在代码中使用任务x是安全的。结果
在实现continuations的代码中:
.ContinueWith(tsks=>
{
var resTask1 = Task1.Result;
...
}
, ct)
由于任务
Task1
已完成运行,因此可以保证在不阻塞的情况下获得结果。这里有一种使用ConcurrentDictionary的方法,听起来它可能适用于您的用例。此外,由于您是并发新手,它还向您显示了联锁类:
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>();
}
类程序
{
静态void Main(字符串[]参数)
{
Console.WriteLine(“正在执行…”);
var-numotasks=50;
var tasks=新列表();
for(int i=0;i
{
var计数器=联锁增量(参考计数器);
控制台。写线(计数器);
if(计数器==numotasks-1)
{
Console.WriteLine(“等待{0}ms”,5000);
Task.Delay(5000.Wait();//用于模拟长时间运行的任务
}
_State.AddOrUpdate(计数器,“Updated Yo!”,(k,v)=>
{
抛出新的InvalidOperationException(“这不应该发生多次。”);
});
});
任务。添加(iTask);
}
Task.WhenAll(任务)
.ContinueWith(t=>
{
var longishState=_State[numOfTasks-1];
控制台写入线(longishState);
Console.WriteLine(“Complete.longishState:+longishState”);
});
Console.ReadKey();
}
静态int_计数器=-1;
静态ConcurrentDictionary_State=新ConcurrentDictionary();
}
您得到的输出与此类似(尽管在继续之前等待队列并不总是最后一个):
解决这个问题的一个优雅方法是使用 像这样:
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
var nroftask=;
ConcurrentDictionary结果=新建ConcurrentDictionary();
var屏障=新屏障(nRFtask,(b)=>
{
//这是塔斯卡的工作
//马上
//这里是TaskB的工作,有TaskA的结果以及您可能需要的任何其他任务
});
Task.Run(()=>{Results[1]=Task1.Result;barrier.SignalAndWait();},ct),
...
Task.Run(()=>{Results[nrofttask]=Taskn.Result;barrier.SignalAndWait();},ct
非常感谢您的回复,但是,无论出于何种原因,我无法访问Task1.Result
。我的任务设置如下var Task1=Task.Run(()=>…)
这可能是错误的吗???@JohnBustos当你使用任务时。运行,你得到的任务没有结果。要得到有结果的任务,请使用var Task1=Task.Factory.StartNew(()=>…返回类型的expr…);
@dasblinkenlight Task.Run支持返回值。可能您不知道。异步编程的高级组织方式有多种,例如Rx、TPL。但听起来您可以使用Parallel.For/Foreach/etc。您知道它的存在吗?请参阅底部的备注了解如何使用:您应该能够使用在这里等着,这简化了很多。