回答編集履歴

1

追記

2025/03/17 08:35

投稿

tamoto
tamoto

スコア4260

test CHANGED
@@ -3,7 +3,6 @@
3
3
  ひとまず以下のようなメソッドを作っておけば目的は達成できそうです。
4
4
 
5
5
  ```csharp
6
-
7
6
  private static async Task<T> CancelAfter<T>(Func<CancellationToken, Task<T>> task, CancellationTokenSource cancellation)
8
7
  {
9
8
  var result = await task(cancellation.Token).ConfigureAwait(false);
@@ -27,3 +26,60 @@
27
26
  ```
28
27
 
29
28
  仮に各タスクが失敗で終わる可能性があるとこれだけだとうまくいかないですが、そうでなければ必要十分かと思います。
29
+
30
+ ---
31
+
32
+ 追記:
33
+ 各 Task が完了 (正常終了) した上で戻り値が OK 以外になる場合があると上記の処理では対応できないので、
34
+ 予め各 Task が OK 以外の場合に例外を返すようにしておくことで、以下のようなヘルパー関数で対応できるようになるかと思います。
35
+
36
+ ```csharp
37
+ private static Task<T> WhenAnySucceeded<T>(params IEnumerable<Func<CancellationToken, Task<T>>> tasks)
38
+ {
39
+ var tcs = new TaskCompletionSource<T>();
40
+ var cts = new CancellationTokenSource();
41
+
42
+ _ = Task.Run(async () =>
43
+ {
44
+ try
45
+ {
46
+ await Task.WhenAll(tasks.Select(async task =>
47
+ {
48
+ var result = await task(cts.Token).ConfigureAwait(false);
49
+ cts.Cancel();
50
+ tcs.TrySetResult(result);
51
+ })).ConfigureAwait(false);
52
+ }
53
+ catch (TaskCanceledException)
54
+ {
55
+ tcs.TrySetCanceled();
56
+ }
57
+ catch (Exception exception)
58
+ {
59
+ tcs.TrySetException(exception);
60
+ }
61
+ });
62
+
63
+ return tcs.Task;
64
+ }
65
+ ```
66
+
67
+ ```csharp
68
+ // 最初に成功した (値を返した) Task の結果を得る
69
+ var result = await WhenAnySucceeded(
70
+ token => TaskA(token),
71
+ token => TaskB(token),
72
+ token => TaskC(token));
73
+
74
+ ...
75
+
76
+ async Task<string> TaskA(CancellationToken token)
77
+ {
78
+ ...
79
+ if (status == "OK") // status が OK でない場合は例外を発生させることで Task を失敗にする
80
+ return result;
81
+ else
82
+ throw new Exception("TaskA failed");
83
+ }
84
+ ```
85
+