Task.WhenAllでは、例外をすぐにキャッチすることはできません。

スイス

UDPクライアントからデータを受信するためのUDPソケットを作成するクラスのインスタンスが2つあります。インスタンスの1つが例外をスローした場合、上位層ですぐに処理したいと思います。私のプログラムでは、それらはで始まりますawait 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;
}

更新1: Task.WhenAnyが実行可能なソリューションになります。ありがとう@CamiloTerevinto

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

  cts.Cancel();
}

更新2:すべてのタスクのよりきめ細かい例外処理については、@ Servyによって提案されたTask.WhenAllの独自の適応実装を使用します。

サービー

動作はフレームワークのWhenAll実装とは十分に異なるため、独自の適応バージョンを作成するのがおそらく最善ですが、幸いなことに、実装は特に難しくありません。すべてのタスクに継続を添付するだけです。キャンセルまたは障害が発生した場合、結果のタスクは同じことを行い、成功した場合は結果を保存し、最後のタスクが成功した場合は、保存されたすべての結果を使用してタスクを完了します。 。

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

多くのタスクベースの一般的な操作と同様に、結果のないバージョンも必要です。顕著なオーバーヘッドを処理したくない場合は、結果ベースのアプローチをコピーして貼り付けるだけで、すべての結果は引き裂かれましたが、それは難しいことではなく、ただエレガントではありません。これらすべてのタスクを結果のあるタスクに変換することもできますが、このような操作の場合、オーバーヘッドが問題になる可能性があります。

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

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

タイプ「void」を「System.Threading.Tasks.Task」に暗黙的に変換することはできません

分類Dev

タイプ 'System.Threading.Tasks.Task'を 'string'に暗黙的に変換することはできません

分類Dev

タイプ「bool」を「System.Threading.Tasks.Task」に暗黙的に変換することはできません

分類Dev

演算子*をオペランドintおよびTask <int>に適用することはできません

分類Dev

Task.WhenAllを適切にキャンセルし、最初の例外をスローする方法は?

分類Dev

Task.WhenAllで例外が発生した場合にキャンセルして例外を発生させる方法は?

分類Dev

Task.WhenAll を待っているように見えますが、コードの次の行に到達することはありません

分類Dev

urllib2で例外をキャッチすることはできません

分類Dev

「Task.WhenAll」を使用するときにスレッド数を制御するにはどうすればよいですか

分類Dev

タスクの1つが失敗したときに、Task.WhenAllから成功した結果を取得することは可能ですか?

分類Dev

タイプ「System.Threading.Tasks.Task」を「Windows.Foundation.IAsyncAction」に暗黙的に変換することはできません。明示的な変換が存在します

分類Dev

SignalRでタイプ 'System.Threading.Tasks.Task <object>'を 'string'に暗黙的に変換することはできません

分類Dev

Task.WhenAllの後の.NetCoreでは、データを取得するのは.Resultで問題ありませんか、それともデータを取得するためにもう一度待機する必要がありますか?

分類Dev

タイプ「Observable <Task>」はタイプ「Observable <Task []>」に割り当てることができません

分類Dev

UIコントロールの値をパラメーターとしてTask.Factory.StartNew内のメソッドに渡すことはできません

分類Dev

Task.Run()が期待どおりに機能しない-「void型をオブジェクトに暗黙的に変換することはできません」

分類Dev

タイプ 'System.Threading.tasks.task <Quartz.Scheduler>を<Quartz.IScheduler>に暗黙的に変換することはできません。

分類Dev

Task.WhenAllでタスクが完了したときのコールバックはありますか

分類Dev

TaskCanceledExceptionをキャッチしてTask.Canceledをチェックするのは良い考えですか?

分類Dev

Task.Whenall(List <Task>)はOutOfRangeExceptionを出力します

分類Dev

Task.WhenAllは、Task <ConcurrentDictionary>の重複を作成します

分類Dev

C ++では、ユーザーに例外をキャッチさせることはできますか?

分類Dev

例外「テクスチャをnullにすることはできません」DirectX

分類Dev

Task <T>はWebApiで応答しませんが、タスク以外を返すことは機能します

分類Dev

Task.WhenAllは配列ではなくリストを返します

分類Dev

コントローラの同期コードでTask.Runを使用してスループットを向上させることはできますか?

分類Dev

await Task.Run()を使用するAsycメソッドが「完了する」ことはありません

分類Dev

タイプ「Task <Derived>」を「Task <Interface>」に変換できません

分類Dev

Task <List <TEntity >>をTask <IList <TEntity >>に変換できません

Related 関連記事

  1. 1

    タイプ「void」を「System.Threading.Tasks.Task」に暗黙的に変換することはできません

  2. 2

    タイプ 'System.Threading.Tasks.Task'を 'string'に暗黙的に変換することはできません

  3. 3

    タイプ「bool」を「System.Threading.Tasks.Task」に暗黙的に変換することはできません

  4. 4

    演算子*をオペランドintおよびTask <int>に適用することはできません

  5. 5

    Task.WhenAllを適切にキャンセルし、最初の例外をスローする方法は?

  6. 6

    Task.WhenAllで例外が発生した場合にキャンセルして例外を発生させる方法は?

  7. 7

    Task.WhenAll を待っているように見えますが、コードの次の行に到達することはありません

  8. 8

    urllib2で例外をキャッチすることはできません

  9. 9

    「Task.WhenAll」を使用するときにスレッド数を制御するにはどうすればよいですか

  10. 10

    タスクの1つが失敗したときに、Task.WhenAllから成功した結果を取得することは可能ですか?

  11. 11

    タイプ「System.Threading.Tasks.Task」を「Windows.Foundation.IAsyncAction」に暗黙的に変換することはできません。明示的な変換が存在します

  12. 12

    SignalRでタイプ 'System.Threading.Tasks.Task <object>'を 'string'に暗黙的に変換することはできません

  13. 13

    Task.WhenAllの後の.NetCoreでは、データを取得するのは.Resultで問題ありませんか、それともデータを取得するためにもう一度待機する必要がありますか?

  14. 14

    タイプ「Observable <Task>」はタイプ「Observable <Task []>」に割り当てることができません

  15. 15

    UIコントロールの値をパラメーターとしてTask.Factory.StartNew内のメソッドに渡すことはできません

  16. 16

    Task.Run()が期待どおりに機能しない-「void型をオブジェクトに暗黙的に変換することはできません」

  17. 17

    タイプ 'System.Threading.tasks.task <Quartz.Scheduler>を<Quartz.IScheduler>に暗黙的に変換することはできません。

  18. 18

    Task.WhenAllでタスクが完了したときのコールバックはありますか

  19. 19

    TaskCanceledExceptionをキャッチしてTask.Canceledをチェックするのは良い考えですか?

  20. 20

    Task.Whenall(List <Task>)はOutOfRangeExceptionを出力します

  21. 21

    Task.WhenAllは、Task <ConcurrentDictionary>の重複を作成します

  22. 22

    C ++では、ユーザーに例外をキャッチさせることはできますか?

  23. 23

    例外「テクスチャをnullにすることはできません」DirectX

  24. 24

    Task <T>はWebApiで応答しませんが、タスク以外を返すことは機能します

  25. 25

    Task.WhenAllは配列ではなくリストを返します

  26. 26

    コントローラの同期コードでTask.Runを使用してスループットを向上させることはできますか?

  27. 27

    await Task.Run()を使用するAsycメソッドが「完了する」ことはありません

  28. 28

    タイプ「Task <Derived>」を「Task <Interface>」に変換できません

  29. 29

    Task <List <TEntity >>をTask <IList <TEntity >>に変換できません

ホットタグ

アーカイブ