C# 并行执行任务

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

好的,所以基本上我有一堆任务(10),我想同时启动它们并等待它们完成。完成后,我想执行其他任务。我读了很多关于这方面的参考资料,但我无法将其正确地用于我的特定案例

以下是我目前拥有的(代码已简化):

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));
        }
    }