2

私はこれを本当に理解していません。以下の私のコードでは、token.ThrowIfCancellationRequested()によってスローされた例外がそのまま実行され、Mainが.Wait()を使用している場所に返送されず、代わりに、スタックトレースが実際にどこで発生したかを知らずにその場でスローされます「タスクがキャンセルされました」以外。ただし、asyncキーワードを削除し、awaitTask.Delay()を使用してtry catchブロックを削除すると、メインの.Wait()に返送され、そこでキャッチされます。

私は何か間違ったことをしていますか、それともawait Task.Delay()とtoken.ThrowIfCancellationRequested()によってスローされた例外の両方を.Wait()までバブルアップするためにどのように正確に取得しますか?

static void Main(string[] args)
{
    var t = new CancellationTokenSource();
    var task = Task.Factory.StartNew((x) => Monitor(t.Token), t.Token, TaskCreationOptions.LongRunning);

    while (true)
    {
        if (Console.ReadLine() == "cancel")
        {
            t.Cancel();
            try 
            {
                task.Wait();
            }
            catch(AggregateException)
            {
                Console.WriteLine("Exception via task.Wait()");

            }
            Console.WriteLine("Cancelled");
        }
    }
}

static async void Monitor(CancellationToken token)
{
    while(true)
    {
        for(int i = 0; i < 5; i++)
        {
            // If the async in the method signature, this does not send the exception to .Wait();
            token.ThrowIfCancellationRequested();

            // Do Work
            Thread.Sleep(2000);
        }

        // Wait 10 seconds before doing work again.

        // When this try block is removed, and the async is taken out of the method signature,
        // token.ThrowIfCancellationRequested() properly sends the exception to .Wait()
        try
        {
            await Task.Delay(10000, token);
        } 
        catch(TaskCanceledException) 
        {
            Console.WriteLine("Exception from Delay()");
            return;
        }
    }
}
4

1 に答える 1

2

避けるべきasync voidです。その例外処理セマンティクスはトリッキーであり、他のasyncメソッドに構成することはできません。

async voidメソッドは概念的にはイベントハンドラーであるため、例外をスローすると、そのメソッドで直接発生しSynchronizationContextます。この場合は、スレッドプールスレッドでスローされます。

-returningメソッドの非同期版は、void-returningメソッドではありませんasync void。これはasync Task-returningメソッドです。したがって、Monitorメソッドは次を返す必要がありますTask

static void Main(string[] args)
{
  var t = new CancellationTokenSource();
  var task = Monitor(t.Token);

  while (true)
  {
    if (Console.ReadLine() == "cancel")
    {
      t.Cancel();
      try 
      {
        task.Wait();
      }
      catch(AggregateException)
      {
        Console.WriteLine("Exception via task.Wait()");
      }
      Console.WriteLine("Cancelled");
    }
  }
}

static async Task Monitor(CancellationToken token)

LongRunningフラグを見逃すことを心配しないでください。これは単なる最適化であり、スレッドプールはそれがなくても正常に機能します。

私のasync/awaitイントロまたは公式のMSDNドキュメントが役立つ場合があります。

于 2012-12-04T13:23:23.887 に答える