回答編集履歴
1
追記
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
|
+
|