前提・実現したいこと
非同期で実行したいタスクがありますが、そのタスクは同時に複数動かしたくありません。
先に実行されているタスクがあった場合は、そのタスクをキャンセルして、終了を待ってから新しいタスクを実行したいです。
以下のように作ってみて一応動作しているものの、非同期処理の知識が無いためこの作り方であっているのか自信がありません。
もっと適した作り方があると思うのですが、皆さんならどう作られますか?
該当のソースコード
C#
1private int taskNo = 0; 2 3private List<CancellationTokenSource> cancelTokenSources = new List<CancellationTokenSource>(); 4 5private async void button1_Click(object sender, EventArgs e) 6{ 7 var no = this.taskNo++; 8 9 CancellationTokenSource cts = null; 10 try 11 { 12 // 自身の CancellationTokenSource を生成してリストに登録 13 lock (cancelTokenSources) 14 { 15 cts = new CancellationTokenSource(); 16 cancelTokenSources.Add(cts); 17 } 18 19 // 先に実行しているタスクが終了するまで待機するループ 20 while (true) 21 { 22 lock (cancelTokenSources) 23 { 24 // 最後に登録されたCancellationTokenSource以外はキャンセルする 25 for (var i = 0; i < (cancelTokenSources.Count - 1); i++) 26 { 27 cancelTokenSources[i].Cancel(); 28 } 29 30 // キャンセル途中のCancellationTokenSourceがなければループを抜ける 31 if (cancelTokenSources.Count(n => n.IsCancellationRequested) <= 0) { break; } 32 } 33 34 // 自身がキャンセルされた場合はループを抜ける 35 if (cts.IsCancellationRequested) { break; } 36 37 await Task.Delay(100); 38 }; 39 40 // 自身がキャンセルされていなければタスクを実行する 41 // 自身がキャンセルされている場合はタスクの生成すらしない 42 if (cts.IsCancellationRequested == false) 43 { 44 int r = await Task1(cts.Token, no); 45 } 46 } 47 finally 48 { 49 // 自身の CancellationTokenSource をリストから削除して廃棄 50 if (cts != null) 51 { 52 lock (cancelTokenSources) 53 { 54 cancelTokenSources.Remove(cts); 55 cts.Dispose(); 56 } 57 } 58 } 59} 60 61// 非同期だが同時に複数実行したくないタスク(内容に意味はない) 62private async Task<int> Task1(CancellationToken token, int no) 63{ 64 Console.WriteLine(no + ":start"); 65 for (int i = 0; i < 10; i++) 66 { 67 Console.WriteLine(no + ":" + i); 68 if (token.IsCancellationRequested) 69 { 70 Console.WriteLine(no + ":cancel"); 71 return -1; 72 } 73 await Task.Delay(1000); 74 } 75 Console.WriteLine(no + ":end"); 76 return 1; 77}
補足情報(FW/ツールのバージョンなど)
Visual Studio Community 2017
.NET Framework 4.7
追記
実行しやすいようにと、私が不用意にサンプルコードをフォームアプリケーションで書いてしまったために、ユーザーによる操作を想定していると誤解させてしまいました。
実際には button1_Clickメソッドの部分は他のプログラムから非常に素早く連続実行される可能性も想定しています。
極端に言えば以下のように並列実行されても問題なく4つがキャンセルされて、最後の1つだけ実行が続く感じです。
C#
1Parallel.Invoke( 2 () => button1_Click(null, null), 3 () => button1_Click(null, null), 4 () => button1_Click(null, null), 5 () => button1_Click(null, null), 6 () => button1_Click(null, null) 7);
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/10/16 10:55
2018/10/16 11:35
2018/10/18 01:37
2018/10/18 03:42
2018/10/18 03:49
2018/10/18 04:12
2018/10/18 04:13
2018/10/18 04:34
2018/10/18 04:36