避免在foreach循环中等待

时间:2018-06-17 05:21:07

标签: c# .net performance async-await

我正在尝试优化此代码以减少完成for循环所需的时间。在这种情况下,CreateNotification()需要很长时间,并且使用async await并不会提高性能,因为正在等待每个异步调用。我想使用Task.WhenAll()来优化代码。我怎样才能做到这一点?

foreach (var notification in notificationsInput.Notifications)
{
  try
  {
    var result = await CreateNotification(notification);
    notification.Result = result;          
  }
  catch (Exception exception)
  {
    notification.Result = null;
  }
  notifications.Add(notification);
}

3 个答案:

答案 0 :(得分:3)

您可以在要并行处理其元素的集合上调用Select,并将异步委托传递给它。此异步委托将为每个已处理的元素返回Task,因此您可以在所有这些任务上调用Task.WhenAll。模式是这样的:

var tasks = collection.Select(async (x) => await ProcessAsync(x));
await Task.WhenAll(tasks);

对于你的例子:

var tasks = notificationsInput.Notifications.Select(async (notification) =>
{
    try
    {
        var result = await CreateNotification(notification);
        notification.Result = result;          
    }
    catch (Exception exception)
    {
        notification.Result = null;
    }
});
await Task.WhenAll(tasks);

这假定CreateNotification是线程安全的。

答案 1 :(得分:1)

更新

您需要安装DataFlow才能使用此解决方案

https://www.nuget.org/packages/System.Threading.Tasks.Dataflow/

取决于CreateNotification是什么以及是否要并行运行。

您可以使用DataFlow ActionBlock,如果这是 IO绑定或Mix IO / CPU绑定操作,它将为您提供两全其美的优势让你运行async并行并行

public static async Task DoWorkLoads(NotificationsInput notificationsInput)
{
   var options = new ExecutionDataflowBlockOptions
                     {
                        MaxDegreeOfParallelism = 50
                     };

   var block = new ActionBlock<Something>(MyMethodAsync, options);

   foreach (var notification in notificationsInput.Notifications)
      block.Post(notification);

   block.Complete();
   await block.Completion;

}

...

public async Task MyMethodAsync(Notification notification)
{       
     var result = await CreateNotification(notification);
     notification.Result = result;    
}

加入胡椒和盐调味。

答案 2 :(得分:1)

我认为这应该等同于你的代码:

var notifications = new ConcurrentBag<Notification>();
var tasks = new List<Task>();
foreach (var notification in notificationsInput.Notifications)
{
    var task = CreateNotification(notification)
                    .ContinueWith(t =>
                    {
                        if (t.Exception != null)
                        {
                            notification.Result = null;
                        }
                        else
                        {
                            notification.Result = t.Result;
                        }
                        notifications.Add(notification);
                    });
    tasks.Add(task);
}
await Task.WhenAll(tasks);

.ContinueWith(将从CreateNotification(收到已完成/失败的任务,并且本身就是一项任务。我们将ContinueWith任务添加到列表中,并在WhenAll(中使用该任务。

我使用ConcurrentBag进行通知,以便您可以安全地从多个线程添加。如果要将其转换为常规列表,可以调用var regularListNotifications = notifications.ToList();(假设您使用LINQ)。