2

ドキュメント CancellationTokenSource.Cancel を読んだとき、例外をスローすることは想定されていません。
CancellationTokenSource.Cancel

cts.Cancel(); の呼び出しの下 OperationCanceledException を引き起こしている (スローしていない)。
その行をコメントアウトしたかのように、最後の OperationCanceledException がスローされないことを非常に確信しています。

cts.Cancel 行がアクティブな場合、例外をスローしている行は t2.Wait(token); です。
cts.Cancel(); に遅延がある場合 次に t2.Wait(トークン); 行が呼び出されるとすぐに例外をスローしません。
t2.Wait(トークン); cts.Cancel() が実行されたときにのみその例外をスローします。
それは適切な行動ですか?
一貫性がある場合は、それで問題ありませんが、cts.Cancel で例外が発生することは望ましくありません。
私は明らかに混乱しています。動作を理解したいだけなので、これを本番環境に快適に持ち込むことができます。
現在、BackGroundWorker を使用してこれを行っており、wait と cancel を使用して簡単に追跡および維持できると考えました。

if (token.IsCancellationRequested || ctr == 100000000)
まだ ctr == 100000000
でスローする

このコードに何か問題がありますか?
それとも、それがどのように機能するはずですか?

Task t2 = Task.Run に try catch がないと、キャッチされない例外がスローされました。
t2 の try catch でキャッチされると思っていましたが、一度に 1 つずつ質問してください。

これは .NET 4.5 のコンソール アプリです。

static void Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken token = cts.Token;

    Task.Run(() =>
    {
        Thread.Sleep(1000);
        cts.Cancel();  // this is thowing an exception that is caught on the last catch (OperationCanceledException Ex)
        // according to the documentation this is not supposed
        // if I comment out the cts.Cancel(); then the exeption is not thrown
        if (token.IsCancellationRequested)
            Console.WriteLine("Cancellation requested in Task {0}.",
                                Task.CurrentId);
        else
            Console.WriteLine("Cancellation Not requested in Task {0}.",
                                Task.CurrentId);

    }, token);

    //tried this but did not help
    //Task.Run(() =>
    //{
    //    //Thread.Sleep(1000);
    //    if (token.IsCancellationRequested)
    //        Console.WriteLine("Cancellation requested in Task {0}.",
    //                            Task.CurrentId);
    //}, token);
    //Thread.Sleep(1000);
    ////cts.Cancel();


    Task t2 = Task.Run(() =>
    {
        try
        {
            Console.WriteLine("Task t2 started Int32.MaxValue = " + Int32.MaxValue.ToString());
            Thread.Sleep(4000);
            for (int ctr = 0; ctr < Int32.MaxValue; ctr++)
            {
                if (ctr < 100 || ctr % 100000000 == 0)
                {
                    Console.WriteLine(ctr.ToString());

                }
                if (token.IsCancellationRequested || ctr == 100000000)  //  || ctr == 100000000
                {
                    Console.WriteLine("ThrowIfCancellationRequested in t2 Task  {0}.",
                                Task.CurrentId);
                    throw new OperationCanceledException(token);
                    //token.ThrowIfCancellationRequested();
                }
            }
            Console.WriteLine("Task {0} finished.",
                            Task.CurrentId);
        }
        catch (OperationCanceledException Ex)
        {
            //Console.WriteLine(Ex.ToString());
            Console.WriteLine("OperationCanceledException in Task t2 {0}: The operation was cancelled.",
                                Task.CurrentId);
        }
        catch (Exception Ex)
        {
            Console.WriteLine("Task t2 = Task.Run Exception Ex" + Ex.Message);
        }               
    });
    try
    {
        Console.WriteLine("t2.Wait a");
        t2.Wait(token);
    }
    catch (AggregateException e)
    {
        Console.WriteLine("AggregateException");
        foreach (var v in e.InnerExceptions)
            Console.WriteLine(e.Message + " " + v.Message);
    }
    catch (OperationCanceledException Ex)
    {
        //Console.WriteLine(Ex.ToString());
        Console.WriteLine("OperationCanceledException in Task {0}: The operation was cancelled.",
                            t2.Id);
    }
    catch (Exception Ex)
    {
        Console.WriteLine(Ex.ToString());
    }
    Console.WriteLine("end");
    Console.ReadLine();
}
4

3 に答える 3

4

はい、これは予想される動作です。Task.Waitキャンセル トークンを受け取る のオーバーロードは、次の状態になるまで待機します。

  • 待機中のタスクが完了しました
  • 渡されたキャンセル トークンはキャンセルされます。

後者の場合、Task.Wait渡されたトークンがキャンセルされたことを確認すると、「OperationCancelledException」がスローされます。あなたのケースで例外がトリガーされたときのコールスタックは次のとおりです

    mscorlib.dll!System.Threading.CancellationToken.ThrowOperationCanceledException() Line 505 + 0x4d bytes C#
    mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 642 C#
    mscorlib.dll!System.Threading.Tasks.Task.SpinThenBlockingWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3284 + 0xf bytes  C#
    mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3223 + 0x10 bytes C#
    mscorlib.dll!System.Threading.Tasks.Task.Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3129 + 0xc bytes  C#
    mscorlib.dll!System.Threading.Tasks.Task.Wait(System.Threading.CancellationToken cancellationToken) Line 3064 + 0xe bytes   C#
>   ConsoleApplication1.exe!ConsoleApplication1.Program.Main(string[] args) Line 82 + 0x15 bytes    C#

質問の 2 番目の部分について: t2 内で try/catch を削除し、トークンをキャンセルしなかった場合、

AggregateException次に、外部ハンドラーに着陸することになります(への呼び出しt2.Waitは、スローしたAggregateException内部例外を 1 つスローするためOperationCanceledExceptionです。

于 2014-03-17T18:22:43.333 に答える
1

これについては間違っているかもしれませんが、この例外が発生する理由は、cancel ステートメントを入れた同じタスクをキャンセルしているためです。operationCanceledException MSDN ドキュメントには、次のように記載されています。

スレッドが実行していた操作のキャンセル時にスレッドでスローされる例外。

最初のタスク呼び出しを次のように変更してみて、それでも例外が発生するかどうかを確認してください:-

Task.Run(() =>
    {
        Thread.Sleep(1000);
        if (token.IsCancellationRequested)
            Console.WriteLine("Cancellation requested in Task {0}.",
                                Task.CurrentId);
    }, token);
cts.Cancel();

taskCancellationSource の役立つリンク:-

http://johnbadams.wordpress.com/2012/03/10/understanding-cancellationtokensource-with-tasks/

于 2014-03-17T16:40:50.433 に答える