前提・実現したいこと
TaskのWhenAny
のキャンセルについて質問があります.
Microsoft Docsを参考にして,あるタスクが完了した後に残りのタスクのキャンセルを実装しました.
しかし,ループの中でWhenAny
を使用する際に,トークンがIsCancellationRequested = true
になってしまっているため,2回目以降うまく動いてくれません.
やりたいことは,UI側でボタンをクリックしたときのキャンセルと,WhenAny
であるタスクが完了したあとの残りのタスクのキャンセルの判別です.
該当のソースコード
C#
1CancellationTokenSource cts; 2 3void startButton_Click(object sender, RoutedEventArgs e) 4{ 5 cts = new CancellationTokenSource(); 6 var _ = Hoge(cts.Token); 7} 8 9void stopButton_Click(object sender, RoutedEventArgs e) 10{ 11 cts?.Cancel(); 12} 13 14async Task Hoge(CancellationToken token) 15{ 16 try 17 { 18 await Fuga(token); 19 } 20 catch (OperationCanceledException) 21 { 22 Console.WriteLine("キャンセルされたよ"); 23 } 24 finally 25 { 26 cts.Dispose(); 27 cts = null; 28 } 29} 30 31async Task Fuga(CancellationToken token) 32{ 33 for (int i = 0; i < 5; i++) 34 { 35 var task1 = Task.Run(() => Method1(token), token); 36 var task2 = Task.Run(() => Method2(token), token); 37 38 var firstFinishedTask = await Task.WhenAny(task1, task2); 39 cts.Cancel(); 40 await firstFinishedTask; 41 } 42} 43 44void Method1(CancellationToken token) 45{ 46 // 重い処理 47 token.ThrowIfCancellationRequested(); 48} 49void Method2(CancellationToken token) 50{ 51 // 重い処理 52 token.ThrowIfCancellationRequested(); 53}
試したこと
CancellationTokenSource.CreateLinkedTokenSource
を使ってみて,以下のようなコードを試してみましたが,全てinnerCts
でのキャンセルとして判別され,ボタンでのキャンセルができませんでした....
C#
1CancellationTokenSource innerCts; 2 3async Task Hoge(CancellationToken token) 4{ 5 try 6 { 7 await Fuga(token); 8 } 9 catch (OperationCanceledException) 10 { 11 if (innerCts.Token.IsCancellationRequested) 12 Console.WriteLine("innerCtsのトークンでキャンセルされたよ"); 13 else if (token.IsCancellationRequested) 14 Console.WriteLine("ctsのトークンでキャンセルされたよ"); 15 } 16 finally 17 { 18 cts.Dispose(); 19 cts = null; 20 } 21} 22 23async Task Fuga(CancellationToken token) 24{ 25 for (int i = 0; i < 5; i++) 26 { 27 innerCts = new CancellationTokenSource(); 28 using(CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(token, innerCts.Token)) 29 { 30 var task1 = Task.Run(() => Method1(linkedCts.Token), linkedCts.Token); 31 var task2 = Task.Run(() => Method2(linkedCts.Token), linkedCts.Token); 32 33 var firstFinishedTask = await Task.WhenAny(task1, task2); 34 innerCts.Cancel(); 35 await firstFinishedTask; 36 } 37 } 38}
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。