首页>Program>source

好吧,所以基本上我有很多任务(10),我想同时启动所有任务并等待它们完成.完成后,我想执行其他任务.我读了很多有关此的资源,但是我无法针对我的具体情况...

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

public async Task RunTasks()
{
    var tasks = new List<Task>
    {
        new Task(async () => await DoWork()),
        //and so on with the other 9 similar tasks
    }

    Parallel.ForEach(tasks, task =>
    {
        task.Start();
    });
    Task.WhenAll(tasks).ContinueWith(done =>
    {
        //Run the other tasks
    });
}
//This function perform some I/O operations
public async Task DoWork()
{
    var results = await GetDataFromDatabaseAsync();
    foreach (var result in results)
    {
        await ReadFromNetwork(result.Url);
    }
}

所以我的问题是,当我等待任务完成时, 呼叫,它告诉我所有任务都结束了,即使它们都没有完成.我尝试添加 WhenAll 在我的飞天 当我输入继续任务时,数据不断从我以前的 Console.WriteLine中输入 尚未真正完成。

我在这里做什么错了?

foreach
最新回答
  • 2021-1-11
    1 #

    您几乎永远都不要使用 Task 直接构造函数.对于您而言,该任务只会触发您迫不及待的实际任务。

    您可以简单地致电 DoWork 然后取回任务,将其存储在列表中,然后等待所有任务完成.含义:

    tasks.Add(DoWork());
    // ...
    await Task.WhenAll(tasks);
    

    但是,异步方法将同步运行,直到第一次等待未完成的任务.如果您担心该部分花费的时间太长,请使用 Task.Run 将其卸载到另一个 ThreadPool 线程,然后存储 that 列表中的任务:

    tasks.Add(Task.Run(() => DoWork()));
    // ...
    await Task.WhenAll(tasks);
    

  • 2021-1-11
    2 #

    本质上,您是在混合两种不兼容的异步范例; 即 Parallel.ForEach()async-await

    对于您想要的,执行一项或另一项.例如.你可以只用 Parallel.For[Each]() 并完全删除异步等待. Parallel.For[Each]() 仅在所有并行任务完成后才返回,然后您可以移至其他任务。

    代码也有其他一些问题:

      you mark the method async but don't await in it (the await you do have is in the delegate, not the method);

      you almost certainly want .ConfigureAwait(false) 随时待命,特别是如果您不想立即在UI线程中使用结果。

      p

      如果要使用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
      }
      

      这些方法仅并行处理少量代码:将方法排队到线程池中以及返回未完成的 Task .同样,对于如此少量的任务,并行化可能比异步运行花费更多的时间.仅当您的任务在第一次等待之前做一些更长的(同步)工作时,这才有意义。

      在大多数情况下,更好的方法是:

      public async Task RunTasks()
      {
          await Task.WhenAll(new [] 
          {
              DoWork(),
              //...
          });
          //Run the other tasks
      }
      

      在您的代码中,我认为:

      您不应将代码包装在 Task中 在传递到 Parallel.ForEach之前

      您可以 await Task.WhenAll 而不是使用 ContinueWith

  • 2021-1-11
    3 #

    DoWork method是异步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            
    }
    

    几乎不应该使用 Task 构造函数来创建新任务.要创建异步I / O任务,只需调用 async 方法.要创建将在线程池线程上执行的任务,请使用 Task.Run .您可以阅读这篇文章,详细了解 Task.Run 以及创建任务的其他选项。

  • 2021-1-11
    4 #

    还可以在Task.whenAll

    周围添加一个try-catch块。

    NB:抛出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));
        }
    }
    

  • 在C ++中的变量声明中使用struct关键字
  • python:pandas时代三角洲