C# a-同步并行任务的正确方法
目前,我们有一个工作正常的代码:C# a-同步并行任务的正确方法,c#,.net,async-await,task-parallel-library,C#,.net,Async Await,Task Parallel Library,目前,我们有一个工作正常的代码: Result result1 = null; Result result2 = null; var task1 = Task.Factory.StartNew(()=> { var records = DB.Read(".."); //Do A lot result1 = Process(records); }); var task2 = Task.Factory.StartNew(()=> { var record
Result result1 = null;
Result result2 = null;
var task1 = Task.Factory.StartNew(()=>
{
var records = DB.Read("..");
//Do A lot
result1 = Process(records);
});
var task2 = Task.Factory.StartNew(()=>
{
var records = DB.Read(".....");
//Do A lot
result2 = Process(records);
});
Task.WaitAll(task1, task2);
var result = Combine(result1, result2);
现在,我们想使用DB函数的异步对应项,我们正在使用这种新模式:
Result result1 = null;
Result result2 = null;
var task1 = await Task.Factory.StartNew( async ()=>
{
var records = await DB.ReadAsync("..");
//Do A lot
result1 = Process(records);
});
var task2 = await Task.Factory.StartNew(async ()=>
{
var records = await DB.ReadAsync(".....");
//Do A lot
result2 = Process(records);
});
Task.WaitAll(task1, task2);
var result = Combine(result1, result2);
切换到异步后,我们开始观察异常行为。所以我想知道这是否是并行异步调用的正确模式
Task.Factory.StartNew
是一个预异步API。您应该使用Task.Run
,它的设计考虑了异步等待:
var task1 = await Task.Run( async ()=>
{
var records = await DB.ReadAsync("..");
//Do A lot
result1 = Process(records);
});
问题是异步lambda返回一个任务
so任务.Factory.StartNew
返回一个任务
(外部lambda是因为任务.Factory.StartNew
返回一个任务
,内部lambda是异步lambda的结果)
这意味着,当您等待task1
和task2
时,您实际上并不是在等待整个操作,而是等待其中的同步部分
您可以通过在返回的任务上使用任务。展开:
您只需要任务。如果需要将这些方法的同步部分(第一个等待之前的部分)卸载到线程池
启动另一个独立执行单元的新执行任务,则只需运行。因此,最简单的处理方法可能是:
var task1 = Task.Factory.StartNew(()=> //NO AWAIT
{
var records = DB.Read("....."); //NO ASYNC
//Do A lot
result1 = Process(records);
});
... another task definition
Task.WaitAll(task1, task2);
在一个任务中按顺序读取和处理,因为您具有数据依赖性 很好地使用.WaitAll不是异步编程,因为您实际上是在等待时阻塞当前线程。另外,您也不调用.Unwrap,这就是为什么您只需等待异步lambda的创建,而不等待异步lambda本身
Task.Run可以为您展开异步lambda。但是有一种更简单更干净的方法
var task1 = DB.ReadAsync("..").ContinueWith(task => {
//Do A lot
return Process(task.Result);
}, TaskScheduler.Default);
var task2 = DB.ReadAsync("..").ContinueWith(task => {
//Do A lot
return Process(task.Result);
}, TaskScheduler.Default);
var result = Combine(await task1, await task2);
这样,当它准备好时,您将得到准确的结果。因此,您根本不需要额外的任务和变量
请注意,ContinueWith是一个复杂的函数,它在TaskScheduler上工作。如果它不为null,则为Current,否则它在TaskScheduler上工作。默认值为线程池调度器。因此,在调用此函数时,始终显式指定调度程序更安全
同样对于claryfing,我没有包括错误检查,因为实际上DB.ReadAsync可以通过一个错误来完成。但这很容易,你可以自己处理。你具体说的是什么样的异常行为?这些任务相互交织在一起了吗?任务是否工作不正常?您的结果应该是任务返回值,而不是闭包变量。从任务中返回它们。我猜task1/2上等待的方法将立即返回,这可能被称为异常。基本上,我无法遍历调试中的代码。突然间,代码流启动并完成了整个过程,出现了错误您不应该在task1=wait上添加wait。我们认为这就是问题的原因。为什么不直接使用await DB.ReadAsync()
而不使用Task。运行wrappers?@nosratio:对,您仍然可以。但是,看起来Op更关心的是使用任务
,出于某种原因,然后是异步调用。@Tigran,我会使用异步API而不包装,然后用任务包装过程
调用。运行
-如果它们真的是CPU密集型的话。@nosratio:我的观点是:旋转任务
(线程)相对来说是epxnsive操作,考虑到您需要以任何方式(因为它是计算密集型的),只需提前完成即可。更直接的数据管理。@您可以将它们声明为委托,然后调用它们,但它不太干净:Func action1=async()=>。。。;任务task1=action1()
ContinueWith
中的参数是antecedentTask
。StartNew不支持异步方法,它不打开内部任务。您真的应该使用Task.Run(
)来代替。请阅读Stepen ClearI的“”不要使用异步方法,如果您阅读代码,您可能会看到。var task1=Task.Factory.StartNew(异步()=>
您就在那里使用async。task1
将是一个任务
,您的WaitAll
不会像您期望的那样工作。要想像您希望的那样使用WaitAll
,您首先需要在任务上调用.Unwrap()
(或者使用Task.Run(
,它会为您调用Unwrap)@ScottChamberlain你说得对。用手机写代码和错误的代码复制/粘贴。我肯定不是这么想的。
var task1 = Task.Factory.StartNew(()=> //NO AWAIT
{
var records = DB.Read("....."); //NO ASYNC
//Do A lot
result1 = Process(records);
});
... another task definition
Task.WaitAll(task1, task2);
var task1 = DB.ReadAsync("..").ContinueWith(task => {
//Do A lot
return Process(task.Result);
}, TaskScheduler.Default);
var task2 = DB.ReadAsync("..").ContinueWith(task => {
//Do A lot
return Process(task.Result);
}, TaskScheduler.Default);
var result = Combine(await task1, await task2);