Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/256.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 立即捕获异常不适用于Task.WhenAll_C#_Async Await_Task Parallel Library - Fatal编程技术网

C# 立即捕获异常不适用于Task.WhenAll

C# 立即捕获异常不适用于Task.WhenAll,c#,async-await,task-parallel-library,C#,Async Await,Task Parallel Library,我有一个类的两个实例,它创建了一个UDP套接字来接收来自UDP客户端的数据。如果其中一个实例抛出异常,我希望立即在更高的层中处理它。在我的程序中,它们以wait Task.WhenAll(recv1.StartAsync(),recv2.StartAsync)开始。但是,这将等待所有任务完成,然后抛出第一个异常。关于如何解决这个问题有什么想法吗 static async Task Main(string[] args) { var udpReceiver1 = new UdpReceiver

我有一个类的两个实例,它创建了一个UDP套接字来接收来自UDP客户端的数据。如果其中一个实例抛出异常,我希望立即在更高的层中处理它。在我的程序中,它们以
wait Task.WhenAll(recv1.StartAsync(),recv2.StartAsync)
开始。但是,这将等待所有任务完成,然后抛出第一个异常。关于如何解决这个问题有什么想法吗

static async Task Main(string[] args)
{
  var udpReceiver1 = new UdpReceiver(localEndpoint1);
  var udpReceiver2 = new UdpReceiver(localEndpoint2);

  var cts = new CancellationTokenSource();

  try
  {
    await Task.WhenAll(udpReceiver1.StartAsync(cts.Token), udpReceiver2.StartAsync(cts.Token));
  }
  catch (Exception e)
  {
    // Handle Exception...

    cts.Cancel();
  }
}

class UdpReceiver
{
  public UdpReceiver(IPEndPoint endpoint)
  {
    udpClient = new UdpClient(endpoint);
  }

  public async Task StartAsync(CancellationToken cancellationToken)
  {
    try
    {
      while (!cancellationToken.IsCancellationRequested)
      {
        var result = await ReceiveAsync(cancellationToken);
        var message = Encoding.UTF8.GetString(result.Buffer);
        Trace.WriteLine($"UdpClient1 received message:{Encoding.UTF8.GetString(result.Buffer)}");
      
        // throw new Exception("UdpClient1 raising exception");
      }
    }
  }

  private async Task<UdpReceiveResult> ReceiveAsync(CancellationToken cancellationToken)
  {
    var tcs = new TaskCompletionSource<UdpReceiveResult>();
    using (cancellationToken.Register(() => tcs.TrySetCanceled(), false))
    {
      var task = udpClient.ReceiveAsync();

      var completedTask = await Task.WhenAny(task, tcs.Task);

      var result = await completedTask.ConfigureAwait(false);

      return result;
    }
  }

  private UdpClient udpClient;
}

更新2:为了对所有任务进行更细粒度的异常处理,我将使用@Servy提出的Task.Whalls的自适应实现。

可能有一种方法可以做到这一点,但我想不出一种方法,它会使您的代码变得非常混乱。最好在实际任务中处理异常。如果需要使用公共代码处理它,请使用处理程序委托

static async Task Main(string[] args)
{
    var cts = new CancellationTokenSource();

    //This is our common error handler
    void HandleException(Exception ex)
    {
        Log("Exception!" + ex.Message);
        cts.Cancel();
    }

    var udpReceiver1 = new UdpReceiver(localEndpoint1);
    var udpReceiver2 = new UdpReceiver(localEndpoint1);

    //We pass the handler as one of the arguments
    await Task.WhenAll(udpReceiver1.StartAsync(cts.Token, HandleException), udpReceiver2.StartAsync(cts.Token, HandleException));
}

class UdpReceiver
{
  public async Task StartAsync(CancellationToken cancellationToken, Action<Exception> errorHandler)
  {
      try
      {
          while (!cancellationToken.IsCancellationRequested)
          {
              //Main logic goes here
          }
      }
      catch(Exception ex)
      {
          errorHandler(ex);  //Call common error handling code
      }
  }
static async Task Main(字符串[]args)
{
var cts=新的CancellationTokenSource();
//这是我们常见的错误处理程序
无效句柄异常(异常除外)
{
日志(“异常!”+例如消息);
cts.Cancel();
}
var udpreciver 1=新的udpreciver(localEndpoint1);
var udpreciver 2=新的udpreciver(localEndpoint1);
//我们将处理程序作为参数之一传递
wait Task.WhenAll(udpreciver1.StartAsync(cts.Token,HandleException),udpreciver2.StartAsync(cts.Token,HandleException));
}
类前置器
{
公共异步任务StartAsync(CancellationToken CancellationToken,Action errorHandler)
{
尝试
{
而(!cancellationToken.IsCancellationRequested)
{
//主要逻辑在这里
}
}
捕获(例外情况除外)
{
errorHandler(ex);//调用常见错误处理代码
}
}

在所有实现时,该行为与框架完全不同,因此您最好只编写自己的修改版本,幸运的是,实现起来并不特别困难。只需为每个任务附加一个延续,如果任何任务被取消或出现故障,则生成的任务也会执行相同的操作,如果成功,则存储结果t、 如果最后一个任务成功,则使用所有存储的结果完成任务

public static Task<IEnumerable<TResult>> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks)
{
    var listOfTasks = tasks.ToList();
    if (listOfTasks.Count == 0)
    {
        return Task.FromResult(Enumerable.Empty<TResult>());
    }
    var tcs = new TaskCompletionSource<IEnumerable<TResult>>();
    var results = new TResult[listOfTasks.Count];
    int completedTasks = 0;
    for (int i = 0; i < listOfTasks.Count; i++)
    {
        int taskIndex = i;
        Task<TResult> task = listOfTasks[i];
        task.ContinueWith(_ =>
        {
            if (task.IsCanceled)
                tcs.TrySetCanceled();
            else if (task.IsFaulted)
                tcs.TrySetException(task.Exception.InnerExceptions);
            else
            {
                results[taskIndex] = task.Result;
                if (Interlocked.Increment(ref completedTasks) == listOfTasks.Count)
                {
                    tcs.TrySetResult(results);
                }
            }
        });
    }
    return tcs.Task;
}
所有(IEnumerable任务)时的公共静态任务
{
var listOfTasks=tasks.ToList();
if(listOfTasks.Count==0)
{
返回Task.FromResult(Enumerable.Empty());
}
var tcs=new TaskCompletionSource();
var results=new TResult[listOfTasks.Count];
int completedTasks=0;
for(int i=0;i
{
如果(task.IsCanceled)
tcs.trysetconceled();
else if(task.IsFaulted)
TrySetException(task.Exception.innerException);
其他的
{
结果[任务索引]=任务。结果;
if(Interlocked.Increment(ref completedTasks)==listOfTasks.Count)
{
tcs.TrySetResult(结果);
}
}
});
}
返回tcs.Task;
}
与许多基于任务的通用操作一样,您也需要一个没有结果的版本,如果您不想处理显著的开销,您确实需要复制粘贴基于结果的方法,但所有结果都被删除,这并不难,只是不美观。将所有这些任务转换为具有结果的任务也会工作,但对于这样的操作,开销可能是有问题的

public static Task WhenAll(IEnumerable<Task> tasks)
{
    var listOfTasks = tasks.ToList();
    if (listOfTasks.Count == 0)
    {
        return Task.CompletedTask;
    }
    var tcs = new TaskCompletionSource<bool>();
    int completedTasks = 0;
    for (int i = 0; i < listOfTasks.Count; i++)
    {
        int taskIndex = i;
        Task task = listOfTasks[i];
        task.ContinueWith(_ =>
        {
            if (task.IsCanceled)
                tcs.TrySetCanceled();
            else if (task.IsFaulted)
                tcs.TrySetException(task.Exception.InnerExceptions);
            else
            {
                if (Interlocked.Increment(ref completedTasks) == listOfTasks.Count)
                {
                    tcs.TrySetResult(true);
                }
            }
        });
    }
    return tcs.Task;
}
所有(IEnumerable任务)时的公共静态任务
{
var listOfTasks=tasks.ToList();
if(listOfTasks.Count==0)
{
返回Task.CompletedTask;
}
var tcs=new TaskCompletionSource();
int completedTasks=0;
for(int i=0;i
{
如果(task.IsCanceled)
tcs.trysetconceled();
else if(task.IsFaulted)
TrySetException(task.Exception.innerException);
其他的
{
if(Interlocked.Increment(ref completedTasks)==listOfTasks.Count)
{
tcs.TrySetResult(真);
}
}
});
}
返回tcs.Task;
}

您可以分两步等待任务。在第一步中,等待任何任务完成,如果失败,则启动取消。不要在此步骤中处理异常。在等待所有任务完成后,将异常处理延迟到第二步。这两个任务都可能失败,因此,您可能希望分别处理每个异常

Task task1 = udpReceiver1.StartAsync(cts.Token);
Task task2 = udpReceiver2.StartAsync(cts.Token);

// Await any task to complete
Task firstCompletedTask = await Task.WhenAny(task1, task2);
if (firstCompletedTask.IsFaulted) cts.Cancel();

try
{
    // Await them all to complete
    await Task.WhenAll(task1, task2);
}
catch
{
    if (task1.IsFaulted) HandleException(task1.Exception.InnerException);
    if (task2.IsFaulted) HandleException(task2.Exception.InnerException);
}

嗯,
Task.whalll
正是为了让您等待所有任务。您可以使用
Task.whany
但需要自己处理非故障实例,如果一个实例失败,您希望发生什么情况?是否要处理此错误,然后忽略另一个正在运行的实例终止进程?这两个实例都应以gracef结尾如果出现例外情况,我认为您应该在问题中包括您希望两个实例都优雅地终止。这是回答此问题的一个重要信息。这不会导致方法在第一次失败后继续,这是它特别要求的。事实上,这里的用例甚至不适用于处理异常时,它会在到达时立即将其传播给调用方,这可以通过强制捕获并由所讨论的方法处理它来防止发生异常,还可以强制它等待所有操作完成。imall方法的当前名称是否有误导性。可以使用更好的名称当成功或失败时,你可以自由地称之为y
Task task1 = udpReceiver1.StartAsync(cts.Token);
Task task2 = udpReceiver2.StartAsync(cts.Token);

// Await any task to complete
Task firstCompletedTask = await Task.WhenAny(task1, task2);
if (firstCompletedTask.IsFaulted) cts.Cancel();

try
{
    // Await them all to complete
    await Task.WhenAll(task1, task2);
}
catch
{
    if (task1.IsFaulted) HandleException(task1.Exception.InnerException);
    if (task2.IsFaulted) HandleException(task2.Exception.InnerException);
}