C# 并行执行任务
好的,所以基本上我有一堆任务(10),我想同时启动它们并等待它们完成。完成后,我想执行其他任务。我读了很多关于这方面的参考资料,但我无法将其正确地用于我的特定案例 以下是我目前拥有的(代码已简化):C# 并行执行任务,c#,.net,asynchronous,async-await,task-parallel-library,C#,.net,Asynchronous,Async Await,Task Parallel Library,好的,所以基本上我有一堆任务(10),我想同时启动它们并等待它们完成。完成后,我想执行其他任务。我读了很多关于这方面的参考资料,但我无法将其正确地用于我的特定案例 以下是我目前拥有的(代码已简化): public异步任务RunTasks() { var tasks=新列表 { 新任务(异步()=>Wait DoWork()), //其他9项类似的任务也是如此 } Parallel.ForEach(任务,任务=> { task.Start(); }); Task.whall(任务).Continu
public异步任务RunTasks()
{
var tasks=新列表
{
新任务(异步()=>Wait DoWork()),
//其他9项类似的任务也是如此
}
Parallel.ForEach(任务,任务=>
{
task.Start();
});
Task.whall(任务).ContinueWith(完成=>
{
//运行其他任务
});
}
//此函数用于执行一些I/O操作
公共异步任务DoWork()
{
var results=等待GetDataFromDatabaseAsync();
foreach(结果中的var结果)
{
等待ReadFromNetwork(result.Url);
}
}
所以我的问题是,当我在等待任务完成时,whalll
调用会告诉我所有任务都结束了,即使它们都没有完成。我尝试在我的foreach
中添加Console.WriteLine
,当我进入延续任务时,数据不断从我以前的任务中输入,但这些任务并没有真正完成
我做错了什么?您几乎不应该直接使用任务构造函数。在您的情况下,该任务只触发您迫不及待的实际任务
您只需调用DoWork
,返回任务,将其存储在列表中,然后等待所有任务完成。意思是:
tasks.Add(DoWork());
// ...
await Task.WhenAll(tasks);
但是,异步方法同步运行,直到到达未完成任务的第一个等待。如果您担心该部分占用的时间太长,请使用任务。运行将其卸载到另一个线程池线程,然后将该任务存储在列表中:
tasks.Add(Task.Run(() => DoWork()));
// ...
await Task.WhenAll(tasks);
DoWork
方法是一种异步I/O方法。这意味着您不需要多个线程来执行其中的几个线程,因为大部分时间该方法将异步等待I/O完成。一根线就足够了
public async Task RunTasks()
{
var tasks = new List<Task>
{
DoWork(),
//and so on with the other 9 similar tasks
};
await Task.WhenAll(tasks);
//Run the other tasks
}
public异步任务RunTasks()
{
var tasks=新列表
{
销钉(),
//其他9项类似的任务也是如此
};
等待任务。何时(任务);
//运行其他任务
}
您应该尝试创建一个新任务。要创建异步I/O任务,只需调用async
方法。要创建将在线程池线程上执行的任务,请使用task.Run
。您可以阅读有关任务的详细说明。运行
和其他创建任务的选项。如果您想使用TPL在不同线程中并行运行这些任务,您可能需要以下内容:
public async Task RunTasks()
{
var tasks = new List<Func<Task>>
{
DoWork,
//...
};
await Task.WhenAll(tasks.AsParallel().Select(async task => await task()));
//Run the other tasks
}
在你的代码中,我的观点是:
在传递到Parallel.ForEach
之前,不应将代码包装在任务中
您只需等待
任务。当所有
时,您可以使用
而不是使用
继续
本质上,您混合了两种不兼容的异步范例;i、 e.Parallel.ForEach()
和异步等待
为了你想要的,做一件或另一件。例如,您可以只对[每个]()
使用Parallel.For,然后完全删除异步等待<代码>并行。For[Each]()
将仅在所有并行任务完成后返回,然后您可以转到其他任务
该准则还存在一些其他问题:
- 将方法标记为async,但不在其中等待(等待在委托中,而不是方法中)
- 您几乎可以肯定地希望在等待中使用
.ConfigureAwait(false)
,尤其是当您不想在UI线程中立即使用结果时
只需在任务周围添加一个try-catch块即可
注意:抛出System.AggregateException的实例,该实例充当已发生的一个或多个异常的包装器。这对于协调多个任务(如Task.WaitAll()和Task.WaitAny())的方法很重要,这样AggregateException就能够将已发生的所有异常包装到正在运行的任务中
try
{
Task.WaitAll(tasks.ToArray());
}
catch(AggregateException ex)
{
foreach (Exception inner in ex.InnerExceptions)
{
Console.WriteLine(String.Format("Exception type {0} from {1}", inner.GetType(), inner.Source));
}
}
我怀疑在所有这一切中,您在某个地方有一个任务
(直接调用任务构造函数有点可疑),并且您确实希望等待内部任务。您是否可以使用任务。运行而不是任务构造函数/启动Task.Run
将为您进行解包。因此您认为我应该这样做:var tasks=new List{Task.Run(()=>DoWork())}通过同步运行直到第一次等待,您是否希望表示只有在调用Task.whalll
时,它们才会实际异步运行?谢谢@Alisson我指的是DoWork
的同步部分,而不是调用它的代码。@i3arnon,正如您所说,异步方法同步运行,直到到达未完成任务的第一个等待时间。---因此,我可以在这里使用“await task.delay(5).ConfigureAwait(false);语句作为我的DoWork()中的第一行方法?返回主线程并启动线程池线程以执行方法中的重新定义代码,请告知。@saddambinsed您不需要任务。延迟。您有任务。Yield
正是出于此目的。但正如我所见,使用任务。在这种情况下,Run
更好,因为它更明确地说明了启动“一个新任务。这是一个鼓舞人心的代码片段,可能是网络上最好的,它使用PLINQ将任务并行与异步结合起来。建议:使用dotNet 4.7+或Core,您不再需要新[]{}
。相反,您可以只传递多个任务,例如WhenAll(DoWork(),…)
。
public async Task RunTasks()
{
await Task.WhenAll(new []
{
DoWork(),
//...
});
//Run the other tasks
}
try
{
Task.WaitAll(tasks.ToArray());
}
catch(AggregateException ex)
{
foreach (Exception inner in ex.InnerExceptions)
{
Console.WriteLine(String.Format("Exception type {0} from {1}", inner.GetType(), inner.Source));
}
}