等待从任务列表中触发的事件

时间:2014-04-30 21:00:16

标签: c# wcf asynchronous async-await

我正在使用Task.Run()开始几项任务。在每个任务中,我为InstallCompleted定义了一个事件处理程序。然后,我await完成这些任务。棘手的一点是,下面看到的installModule.Install()方法立即返回,因为它在内部调用OneWay WCF服务调用。

那么,在上述每项任务中await的所有实例都触发了各自的installModule事件之前,我该如何InstallCompleted?如果有更好的方法来实现这一目标,我会全力以赴。

var installTasks = new List<Task>();

foreach (var targetMachine in vm.TargetMachines)
{
    var installTask = Task.Run(() =>
    {
        foreach (var module in Modules)
        {
            var installModule = module as IInstallModule;  // copy closure variable
            if (installModule == null)
                continue;

            installModule.InstallCompleted += (s, e) =>
            {
                //TODO set some flag indicating this install is complete
            };

            var ip = new Progress<InstallProgress>();
            installModule.Install(targetMachine.MachineName, ip);
        }
    });

    installTasks.Add(installTask);
}

await Task.WhenAll(installTasks);  // unblocks immediately because installModule.Install() above returns immediately

1 个答案:

答案 0 :(得分:9)

每当您interoperating async with another kind of asynchronous API时,您都可以使用TaskCompletionSource<T>作为异步一次性信号。就个人而言,我喜欢这样做作为一种扩展方法:

public static Task<InstallCompletedResult> InstallAsync(
    this IInstallModule module, string machineName,
    IProgress<InstallProgress> progress = null)
{
  var tcs = new TaskCompletionSource<InstallCompletedResult>();
  InstallCompletedEventHandler handler = null;
  handler = (s, e) =>
  {
    module.InstallCompleted -= handler;
    if (e.Exception != null)
      tcs.TrySetException(e.Exception);
    else
      tcs.TrySetResult(e.Result);
  };
  module.InstallCompleted += handler;
  module.Install(machineName, progress);
  return tcs.Task;
}

一旦你有了互操作件,消费它是非常简单的:

var installTask = Task.Run(async () =>
{
  var tasks = new List<Task>();
  foreach (var module in Modules)
  {
    var installModule = module as IInstallModule;  // copy closure variable
    if (installModule == null)
      continue;

    var ip = new Progress<InstallProgress>();
    tasks.Add(installModule.InstallAsync(targetMachine.MachineName, ip));
  }
});

或者更简单:

var installTask = Task.Run(async () =>
{
  var tasks = Modules.OfType<IInstallModule>()
      .Select(module => module.InstallAsync(targetMachine.MachineName,
          new Progress<InstallProgress>()));
  await Task.WhenAll(tasks);
});