質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

1回答

4413閲覧

TaskのWhenAnyで残りのタスクのキャンセルと外部からのキャンセルを判別する

arw.tyx-out_mz

総合スコア27

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

0グッド

1クリップ

投稿2019/07/27 09:39

前提・実現したいこと

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}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

こんにちは。

何を問題としているのかがわからないのですが、Fuga メソッドが終わる前に innerCts を必ずキャンセルしているので、if 文中で「innerCts がキャンセルされているか?」を判定したら、当然必ず true になります。
実際の目的と思われることは2つ目のコードで既に解決しているように見えますが、その後に書かれた「やりたいこと」が「キャンセルの判別」というのは、何のためにそれが必要なのでしょうか?
参考にちょっと書き換えてみました。どういう振る舞いをするのかをわかりやすくしたつもりです。

diff

1-CancellationTokenSource innerCts; 2- 3-async Task Hoge(CancellationToken token) 4+async Task Hoge() 5 { 6+ cts = new CancellationTokenSource(); // Hoge メソッドのライフサイクルで cts を作る 7 try 8 { 9- await Fuga(token); 10+ await Fuga(cts.Token); 11 } 12 catch (OperationCanceledException) 13 { 14- if (innerCts.Token.IsCancellationRequested) 15- Console.WriteLine("innerCtsのトークンでキャンセルされたよ"); 16- else if (token.IsCancellationRequested) 17+ if (cts.Token.IsCancellationRequested) 18 Console.WriteLine("ctsのトークンでキャンセルされたよ"); 19+ else 20+ Console.WriteLine("それ以外のキャンセル"); // ここに来ることはない 21 } 22 finally 23 { 24 cts.Dispose(); 25 cts = null; 26 } 27 } 28 29 async Task Fuga(CancellationToken token) 30 { 31 for (int i = 0; i < 5; i++) 32 { 33- innerCts = new CancellationTokenSource(); 34+ using (var innerCts = new CancellationTokenSource()) // innerCts はあくまで「内部の」cts 35 using(CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(token, innerCts.Token)) 36 { 37 var task1 = Task.Run(() => Method1(linkedCts.Token), linkedCts.Token); 38 var task2 = Task.Run(() => Method2(linkedCts.Token), linkedCts.Token); 39 40 var firstFinishedTask = await Task.WhenAny(task1, task2); 41 innerCts.Cancel(); 42 await firstFinishedTask; 43 } 44 } 45 } 46

投稿2019/07/31 00:24

tamoto

総合スコア4103

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問