Note
ãã®ããŒãžã«ã¢ã¯ã»ã¹ããã«ã¯ãæ¿èªãå¿ èŠã§ãã ãµã€ã³ã€ã³ãŸãã¯ãã£ã¬ã¯ããªã®å€æŽã詊ãããšãã§ããŸãã
ãã®ããŒãžã«ã¢ã¯ã»ã¹ããã«ã¯ãæ¿èªãå¿ èŠã§ãã ãã£ã¬ã¯ããªã®å€æŽã詊ãããšãã§ããŸãã
ã¿ã¹ã¯ ããŒã¹ã®éåæãã¿ãŒã³ (TAP) ã䜿çšããŠéåææäœãæäœããå Žåã¯ãã³ãŒã«ããã¯ã䜿çšããŠããããã¯ããã«åŸ æ©ãå®çŸã§ããŸãã ã¿ã¹ã¯ã®å Žåããã®ãã¿ãŒã³ã§ã¯ Task.ContinueWith ãªã©ã®ã¡ãœããã䜿çšãããŸãã èšèªããŒã¹ã®éåæãµããŒãã§ã¯ãéåžžã®å¶åŸ¡ãããŒå ã§éåææäœãåŸ æ©ã§ããããã«ããããšã§ã³ãŒã«ããã¯ãé衚瀺ã«ããã³ã³ãã€ã©ã«ãã£ãŠçæãããã³ãŒãã§ãåã API ã¬ãã«ã®ãµããŒããæäŸãããŸãã
Await ã䜿çšããå®è¡ã®äžæ
C# ã® await ããŒã¯ãŒããš Visual Basic ã® Await æŒç®å ã䜿çšããŠã Task ãªããžã§ã¯ããš Task<TResult> ãªããžã§ã¯ããéåæçã«åŸ
æ©ã§ããŸãã
TaskãåŸ
æ©ãããšãawaitåŒã¯voidåã«ãªããŸãã
Task<TResult>ãåŸ
æ©ãããšãawaitåŒã¯TResultåã«ãªããŸãã
awaitåŒã¯ãéåæã¡ãœããã®æ¬äœå
ã§è¡ãå¿
èŠããããŸãã
åŸ
æ©æ©èœã¯ãé ããç¶æ
ã§ç¶ç¶ã䜿çšããŠã¿ã¹ã¯ã«ã³ãŒã«ããã¯ãã€ã³ã¹ããŒã«ããŸãã ãã®ã³ãŒã«ããã¯ã¯ãäžæã®æç¹ã§éåæã¡ãœãããåéããŸãã éåæã¡ãœãããåéããããšãåŸ
æ©äžã®æäœãæ£åžžã«å®äºãã Task<TResult>ã§ãã£ãå Žåã¯ããã® TResult ãè¿ãããŸãã åŸ
ã£ãŠãã Task ãŸã㯠Task<TResult> ã Canceled ç¶æ
ã§çµãã£ãå ŽåãOperationCanceledException äŸå€ãã¹ããŒãããŸãã åŸ
ã£ãŠãã Task ãŸã㯠Task<TResult> ã Faulted ç¶æ
ã§çµãã£ãå Žåããšã©ãŒã®åå ãšãªã£ãäŸå€ãã¹ããŒãããŸãã
Taskã¯è€æ°ã®äŸå€ã®çµæãšããŠãšã©ãŒãçºçããå¯èœæ§ããããŸããããããã®äŸå€ã® 1 ã€ã ããäŒéãããŸãã ãã ãã Task.Exception ããããã£ã¯ããã¹ãŠã®ãšã©ãŒãå«ã AggregateException äŸå€ãè¿ããŸãã
åæã³ã³ããã¹ã (SynchronizationContext ãªããžã§ã¯ã) ããäžææã«éåæã¡ãœãããå®è¡ããŠããã¹ã¬ããã«é¢é£ä»ããããŠããå Žå (ããšãã°ã SynchronizationContext.Current ããããã£ã nullãããŠããªãå Žå)ãéåæã¡ãœããã¯ã³ã³ããã¹ãã® Post ã¡ãœããã䜿çšããŠãåãåæã³ã³ããã¹ãã§åéããŸãã ãã以å€ã®å Žåã¯ãäžææã«çŸåšã®ã¿ã¹ã¯ ã¹ã±ãžã¥ãŒã© (TaskScheduler ãªããžã§ã¯ã) ã«äŸåããŸãã éåžžãããã¯ã¹ã¬ãã ããŒã«ã察象ãšããæ¢å®ã®ã¿ã¹ã¯ ã¹ã±ãžã¥ãŒã© (TaskScheduler.Default) ã§ãã ãã®ã¿ã¹ã¯ ã¹ã±ãžã¥ãŒã©ã¯ãåŸ
æ©äžã®éåææäœãå®äºããå Žæã§åéããããåéãã¹ã±ãžã¥ãŒã«ããããæ±ºå®ããŸãã éåžžãæ¢å®ã®ã¹ã±ãžã¥ãŒã©ã§ã¯ãåŸ
æ©äžã®æäœãå®äºããã¹ã¬ããã§ç¶ç¶ãå®è¡ã§ããŸãã
éåæã¡ãœãããåŒã³åºããšã颿°ã®æ¬äœã¯ãå®äºããŠããªãåŸ
æ©å¯èœãªã€ã³ã¹ã¿ã³ã¹ã§æåã® await åŒã«éãããŸã§åæçã«å®è¡ããããã®æç¹ã§åŒã³åºãã¯åŒã³åºãå
ã«æ»ããŸãã éåæã¡ãœããã voidãè¿ããªãå Žåã¯ãé²è¡äžã®èšç®ã衚ã Task ãŸã㯠Task<TResult> ãªããžã§ã¯ããè¿ããŸãã void 以å€ã®éåæã¡ãœããã§ã¯ãreturn ã¹ããŒãã¡ã³ããæ€åºãããå ŽåããŸãã¯ã¡ãœããæ¬äœã®æ«å°Ÿã«éããå Žåãã¿ã¹ã¯ã¯ RanToCompletion æçµç¶æ
ã§å®äºããŸãã ãã³ãã«ãããªãäŸå€ã«ãã£ãŠå¶åŸ¡ãéåæã¡ãœããã®æ¬äœããé¢ããå Žåãã¿ã¹ã¯ã¯ Faulted ç¶æ
ã§çµäºããŸãã ãã®äŸå€ã OperationCanceledExceptionã®å Žåãã¿ã¹ã¯ã¯ä»£ããã« Canceled ç¶æ
ã§çµäºããŸãã ãã®æ¹æ³ã§ã¯ãçµæãŸãã¯äŸå€ãæçµçã«å
¬éãããŸãã
ãã®åäœã«ã¯ããã€ãã®éèŠãªããªãšãŒã·ã§ã³ããããŸãã ããã©ãŒãã³ã¹äžã®çç±ãããã¿ã¹ã¯ãåŸ æ©ãããŸã§ã«ã¿ã¹ã¯ãæ¢ã«å®äºããŠããå Žåãå¶åŸ¡ã¯çæãããã颿°ã¯åŒãç¶ãå®è¡ãããŸãã ããã«ãå ã®ã³ã³ããã¹ãã«æ»ãããšã¯åžžã«ç®çã®åäœã§ã¯ãªãã倿Žã§ããŸãããã®åäœã«ã€ããŠã¯ã次ã®ã»ã¯ã·ã§ã³ã§è©³ãã説æããŸãã
Yield ãš ConfigureAwait ã䜿çšããäžæãšåéã®æ§æ
ããã€ãã®ã¡ãœããã¯ãéåæã¡ãœããã®å®è¡ããã詳现ã«å¶åŸ¡ããŸãã ããšãã°ã Task.Yield ã¡ãœããã䜿çšããŠãéåæã¡ãœããã« yield ãã€ã³ããå°å ¥ã§ããŸãã
public class Task : âŠ
{
public static YieldAwaitable Yield();
âŠ
}
ãã®ã¡ãœããã¯ãçŸåšã®ã³ã³ããã¹ãã«éåæçã«ãã¹ããŸãã¯ã¹ã±ãžã¥ãŒã«ãæ»ãã®ãšåãã§ãã
public static async Task YieldLoopExample()
{
await Task.Run(async delegate
{
for (int i = 0; i < 1000000; i++)
{
await Task.Yield(); // fork the continuation into a separate work item
}
});
}
Public Async Function YieldLoopExample() As Task
Await Task.Run(Async Function()
For i As Integer = 0 To 999999
Await Task.Yield() ' fork the continuation into a separate work item
Next
End Function)
End Function
ãŸãã Task.ConfigureAwait ã¡ãœããã䜿çšããŠãéåæã¡ãœããã®äžæãšåéãããé©åã«å¶åŸ¡ããããšãã§ããŸãã åè¿°ã®ããã«ãæ¢å®ã§ã¯ãçŸåšã®ã³ã³ããã¹ãã¯éåæã¡ãœãããäžæãããæç¹ã§ãã£ããã£ããããã£ããã£ãããã³ã³ããã¹ãã¯åéæã«éåæã¡ãœããã®ç¶ç¶ãåŒã³åºãããã«äœ¿çšãããŸãã å€ãã®å Žåãããã¯å¿ èŠãªæ£ç¢ºãªåäœã§ãã ãã以å€ã®å Žåã¯ãç¶ç¶ã³ã³ããã¹ããæ°ã«ããããã®ãããªæçš¿ãå ã®ã³ã³ããã¹ãã«æ»ããªãããã«ããããšã§ãããã©ãŒãã³ã¹ãåäžãããããšãã§ããŸãã ãã®åäœãæå¹ã«ããã«ã¯ã Task.ConfigureAwait ã¡ãœããã䜿çšããŠãã³ã³ããã¹ãã§ãã£ããã£ããã³åéããã®ã§ã¯ãªããåŸ æ©äžã®éåææäœãå®äºããå Žæã§å®è¡ãç¶è¡ããããã« await æäœã«éç¥ããŸãã
await someTask.ConfigureAwait(continueOnCapturedContext:false);
AwaitablesãConfigureAwaitãSynchronizationContext
await ã¯ã ã®ã¿ã§ã¯ãªããTaskãæºããä»»æã®åã§åäœããŸãã åã¯ãäºææ§ã®ããGetAwaiterã¡ãœãããæäŸãããã®ã¡ãœãããIsCompletedãOnCompletedãããã³GetResultã¡ã³ããŒãæã€åãè¿ãå ŽåãåŸ
æ©å¯èœã§ãã ã»ãšãã©ã®ãããªã㯠API ã§ã¯ã Taskã Task<TResult>ã ValueTaskããŸã㯠ValueTask<TResult>ãè¿ããŸãã ç¹æ®ãªã·ããªãªã§ã®ã¿ãã«ã¹ã¿ã åŸ
æ©å¯èœå€ã䜿çšããŸãã
ç¶ç¶ã«åŒã³åºãå
ã®ã³ã³ããã¹ããå¿
èŠãªãå Žåã¯ã ConfigureAwait ã䜿çšããŸãã UI ãæŽæ°ããã¢ã㪠ã³ãŒãã§ã¯ãå€ãã®å Žåãã³ã³ããã¹ã ãã£ããã£ãå¿
èŠã§ãã åå©çšå¯èœãªã©ã€ãã©ãª ã³ãŒãã§ã¯ãäžèŠãªã³ã³ããã¹ã ããããåé¿ãããããã¯ããåŒã³åºãå
ã®ããããã㯠ãªã¹ã¯ã軜æžãããããéåžžã ConfigureAwait(false) ããå§ãããŸãã
ConfigureAwait(false) ã¯ãããŒã§ã¯ãªããç¶ç¶ã¹ã±ãžã¥ãŒã«ã倿ŽããŸãExecutionContext ã ã³ã³ããã¹ãã®åäœã®è©³çްã«ã€ããŠã¯ã ExecutionContext ãš SynchronizationContext ã«é¢ããèšäºãåç
§ããŠãã ããã
éåææäœã®åãæ¶ã
åãæ¶ãããµããŒããã TAP ã¡ãœããã¯ããã£ã³ã»ã« ããŒã¯ã³ (CancellationToken ãªããžã§ã¯ã) ãåãå ¥ãããªãŒããŒããŒããå°ãªããšã 1 ã€æäŸããŸãã
ãã£ã³ã»ã« ããŒã¯ã³ ãœãŒã¹ (CancellationTokenSource ãªããžã§ã¯ã) ã䜿çšããŠãã£ã³ã»ã« ããŒã¯ã³ãäœæããŸãã ãœãŒã¹ã® Token ããããã£ã¯ããœãŒã¹ã® Cancel ã¡ãœãããåŒã³åºããããšãã«éç¥ãããã£ã³ã»ã« ããŒã¯ã³ãè¿ããŸãã
var cts = new CancellationTokenSource();
string result = await DownloadStringTaskAsync(url, cts.Token);
⊠// at some point later, potentially on another thread
cts.Cancel();
ããšãã°ã1 ã€ã® Web ããŒãžãããŠã³ããŒãããŠæäœãåãæ¶ãããšãã§ããããã«ããã«ã¯ã CancellationTokenSource ãªããžã§ã¯ããäœæãããã®ããŒã¯ã³ã TAP ã¡ãœããã«æž¡ããæäœãåãæ¶ãæºåãã§ããããœãŒã¹ã® Cancel ã¡ãœãããåŒã³åºããŸãã
var cts = new CancellationTokenSource();
IList<string> results = await Task.WhenAll(from url in urls select DownloadStringTaskAsync(url, cts.Token));
// at some point later, potentially on another thread
âŠ
cts.Cancel();
ãŸãã¯ãåãããŒã¯ã³ãæäœã®éžæçãªãµãã»ããã«æž¡ãããšãã§ããŸãã
var cts = new CancellationTokenSource();
byte [] data = await DownloadDataAsync(url, cts.Token);
await SaveToDiskAsync(outputPath, data, CancellationToken.None);
⊠// at some point later, potentially on another thread
cts.Cancel();
Von Bedeutung
ã©ã®ã¹ã¬ããã§ããã£ã³ã»ã«èŠæ±ãéå§ã§ããŸãã
åãæ¶ãããŒã¯ã³ãåãåãä»»æã®ã¡ãœããã« CancellationToken.None å€ãæž¡ããŠãåãæ¶ããèŠæ±ãããŠããªãããšã瀺ãããšãã§ããŸãã ãã®å€ã«ããã CancellationToken.CanBeCanceled ããããã£ã¯ falseãè¿ããåŒã³åºãããã¡ãœããã¯ããã«å¿ããŠæé©åã§ããŸãã ãã¹ãã®ããã«ã¯ãããŒã¯ã³ãæ¢ã«åãæ¶ãããç¶æ
ãåãæ¶ãäžå¯èœãªç¶æ
ã§éå§ãããã©ããã瀺ãããŒã«å€ãåãåãã³ã³ã¹ãã©ã¯ã¿ãŒã䜿çšããŠã€ã³ã¹ã¿ã³ã¹åãããäºåã«åãæ¶ããããã£ã³ã»ã« ããŒã¯ã³ãæž¡ãããšãã§ããŸãã
ãã£ã³ã»ã«ã«å¯Ÿãããã®ã¢ãããŒãã«ã¯ãããã€ãã®å©ç¹ããããŸãã
åããã£ã³ã»ã« ããŒã¯ã³ãä»»æã®æ°ã®éåææäœãšåææäœã«æž¡ãããšãã§ããŸãã
åããã£ã³ã»ã«èŠæ±ãä»»æã®æ°ã®ãªã¹ããŒã«éä¿¡ã§ããŸãã
éåæ API ã®éçºè ã¯ãåãæ¶ããèŠæ±ã§ãããã©ãããšããã€æå¹ã«ãªãããå®å šã«å¶åŸ¡ã§ããŸãã
API ãæŽ»çšããŠããã³ãŒãã¯ããã£ã³ã»ã«èŠæ±ãå±ããããéåæåŒã³åºããéžæçã«æ±ºå®ã§ããŸãã
é²è¡ç¶æ³ã®ç£èŠ
äžéšã®éåæã¡ãœããã¯ãéåæã¡ãœããã«æž¡ãé²è¡ç¶æ³ã€ã³ã¿ãŒãã§ã€ã¹ãä»ããŠé²è¡ç¶æ³ãå ¬éããŸãã ããšãã°ãããã¹ãã®æååãéåæçã«ããŠã³ããŒããããã®éçšã§ããããŸã§ã«å®äºããããŠã³ããŒãã®å²åãå«ãé²è¡ç¶æ³ã®æŽæ°ãçºçããã颿°ãèããŠã¿ãŸãããã ãã®ãããªã¡ãœããã¯ã次ã®ããã«Windows Presentation Foundation (WPF) ã¢ããªã±ãŒã·ã§ã³ã§äœ¿çšã§ããŸãã
private async void btnDownload_Click(object sender, RoutedEventArgs e)
{
btnDownload.IsEnabled = false;
try
{
txtResult.Text = await DownloadStringTaskAsync(txtUrl.Text,
new Progress<int>(p => pbDownloadProgress.Value = p));
}
finally { btnDownload.IsEnabled = true; }
}
çµã¿èŸŒã¿ã®ã¿ã¹ã¯ ããŒã¹ã®çµã¿åããåã®äœ¿çš
System.Threading.Tasksåå空éã«ã¯ãã¿ã¹ã¯ãäœæããã³æäœããããã®ããã€ãã®ã¡ãœãããå«ãŸããŠããŸãã
泚
ãã®ã»ã¯ã·ã§ã³ã®ããã€ãã®ã³ãŒã ãµã³ãã«ã§ã¯ãBitmap ã䜿çšããŸããããã«ã¯ System.Drawing.Common ããã±ãŒãžãå¿
èŠã§ãããWindowsã§ã®ã¿ãµããŒããããŠããŸãã 圌ãã瀺ãéåæãã¿ãŒã³ã¯ãã¹ãŠã®ãã©ãããã©ãŒã ã«é©çšãããŸããWindows以å€ã®ã¿ãŒã²ããã§ã¯ãã¯ãã¹ãã©ãããã©ãŒã ã®ã€ã¡ãŒãžã³ã°ã©ã€ãã©ãªã䜿çšããããšã«çœ®ãæããŠãã ããã
Task.Run
Task ã¯ã©ã¹ã«ã¯ãRunãšããŠäœæ¥ãç°¡åã«ãªãããŒãããããã¹ã¬ãã ããŒã«ã«Taskãããã§ãããããã€ãã®Task<TResult>ã¡ãœãããå«ãŸããŠããŸãã äŸãã°æ¬¡ãæããããŸãã
public static async Task TaskRunBasicExample()
{
int answer = 42;
string result = await Task.Run(() =>
{
// ⊠do compute-bound work here
return answer.ToString();
});
Console.WriteLine(result);
}
Public Async Function TaskRunBasicExample() As Task
Dim answer As Integer = 42
Dim result As String = Await Task.Run(Function()
' ⊠do compute-bound work here
Return answer.ToString()
End Function)
Console.WriteLine(result)
End Function
ãããã® Run ã¡ãœããã®äžéš ( Task.Run(Func<Task>) ãªãŒããŒããŒããªã©) ã¯ã TaskFactory.StartNew ã¡ãœããã®ç瞮圢ãšããŠååšããŸãã ãã®ãªãŒããŒããŒãã«ããããªãããŒããããäœæ¥å
ã§ await ã䜿çšã§ããŸãã äŸãã°æ¬¡ãæããããŸãã
public static async Task TaskRunAsyncExample()
{
Bitmap image = await Task.Run(async () =>
{
using Bitmap bmp1 = await Stubs.DownloadFirstImageAsync();
using Bitmap bmp2 = await Stubs.DownloadSecondImageAsync();
return Stubs.Mashup(bmp1, bmp2);
});
}
Public Async Function TaskRunAsyncExample() As Task
Dim image As Bitmap = Await Task.Run(Async Function()
Using bmp1 As Bitmap = Await Stubs.DownloadFirstImageAsync()
Using bmp2 As Bitmap = Await Stubs.DownloadSecondImageAsync()
Return Stubs.Mashup(bmp1, bmp2)
End Using
End Using
End Function)
End Function
ãã®ãããªãªãŒããŒããŒãã¯ãã¿ã¹ã¯äžŠåã©ã€ãã©ãªã®TaskFactory.StartNewæ¡åŒµã¡ãœãããšçµã¿åãããŠUnwrap ã¡ãœããã䜿çšããããšãšè«ççã«åçã§ãã
Task.FromResult
ããŒã¿ãæ¢ã«å©çšã§ããåã« FromResult ã«ãªãããããã¿ã¹ã¯ãè¿ãã¡ãœããããè¿ãå¿ èŠãããã·ããªãªã§ã¯ãTask<TResult> ã¡ãœããã䜿çšããŸãã
public static Task<int> GetValueAsync(string key)
{
int cachedValue;
return Stubs.TryGetCachedValue(out cachedValue) ?
Task.FromResult(cachedValue) :
GetValueAsyncInternal(key);
}
static async Task<int> GetValueAsyncInternal(string key)
{
await Task.Delay(1);
return 0;
}
Public Function GetValueAsync(key As String) As Task(Of Integer)
Dim cachedValue As Integer
If Stubs.TryGetCachedValue(cachedValue) Then
Return Task.FromResult(cachedValue)
Else
Return GetValueAsyncInternal(key)
End If
End Function
Private Async Function GetValueAsyncInternal(key As String) As Task(Of Integer)
Await Task.Delay(1)
Return 0
End Function
Task.WhenAll
WhenAll ã¡ãœããã䜿çšããŠãã¿ã¹ã¯ãšããŠè¡šãããè€æ°ã®éåææäœãéåæçã«åŸ
æ©ããŸãã ãã®ã¡ãœããã«ã¯ãäžé£ã®éãžã§ããªã㯠ã¿ã¹ã¯ãŸãã¯äžæ§ã§ãªãäžé£ã®æ±çšã¿ã¹ã¯ããµããŒãããè€æ°ã®ãªãŒããŒããŒãããããŸã (ããšãã°ãè€æ°ã® void ãè¿ãæäœãéåæçã«åŸ
æ©ããããåå€ãç°ãªãå¯èœæ§ãããè€æ°ã®å€ãè¿ãã¡ãœãããéåæçã«åŸ
æ©ãããããŸã)ãäžæ§ãªäžé£ã®ãžã§ããªã㯠ã¿ã¹ã¯ (è€æ°ã® TResultãè¿ãã¡ãœãããéåæçã«åŸ
æ©ãããªã©) ããµããŒãããŸãã
è€æ°ã®é¡§å®¢ã«é»åã¡ãŒã« ã¡ãã»ãŒãžãéä¿¡ãããšããŸãã 1 ã€ã®ã¡ãã»ãŒãžãå®äºããã®ãåŸ ã£ãŠããæ¬¡ã®ã¡ãã»ãŒãžãéä¿¡ããªãããã«ãã¡ãã»ãŒãžã®éä¿¡ãéè€ãããããšãã§ããŸãã éä¿¡æäœãå®äºããã¿ã€ãã³ã°ãšããšã©ãŒãçºçãããã©ããã確èªããããšãã§ããŸãã
IEnumerable<Task> asyncOps = from addr in addrs select SendMailAsync(addr);
await Task.WhenAll(asyncOps);
ãã®ã³ãŒãã¯çºçããå¯èœæ§ãããäŸå€ãæç€ºçã«åŠçãããawait ã®çµæã®ã¿ã¹ã¯ã® WhenAll ããäŸå€ãåæ ããŸãã äŸå€ãåŠçããã«ã¯ã次ã®ãããªã³ãŒãã䜿çšããŸãã
public static async Task WhenAllWithCatch()
{
IEnumerable<Task> asyncOps = from addr in Stubs.addrs select Stubs.SendMailAsync(addr);
try
{
await Task.WhenAll(asyncOps);
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
}
Public Async Function WhenAllWithCatch() As Task
Dim asyncOps As IEnumerable(Of Task) = From addr In Stubs.addrs Select Stubs.SendMailAsync(addr)
Try
Await Task.WhenAll(asyncOps)
Catch exc As Exception
Console.WriteLine(exc)
End Try
End Function
ãã®å Žåãéåææäœã倱æããå Žåããã¹ãŠã®äŸå€ãAggregateExceptionäŸå€ã«çµ±åãããŸãããã®äŸå€ã¯ãTask ã¡ãœããããè¿ãããWhenAllã«æ ŒçŽãããŸãã ãã ãã await ããŒã¯ãŒãã«ãã£ãŠåæ ãããã®ã¯ããããã®äŸå€ã® 1 ã€ã ãã§ãã ãã¹ãŠã®äŸå€ã調ã¹ãå Žåã¯ãåã®ã³ãŒããæ¬¡ã®ããã«æžãçŽãããšãã§ããŸãã
public static async Task WhenAllExamineExceptions()
{
Task[] asyncOps = (from addr in Stubs.addrs select Stubs.SendMailAsync(addr)).ToArray();
try
{
await Task.WhenAll(asyncOps);
}
catch (Exception exc)
{
foreach (Task faulted in asyncOps.Where(t => t.IsFaulted))
{
Console.WriteLine($"Faulted: {faulted.Exception}");
}
}
}
Public Async Function WhenAllExamineExceptions() As Task
Dim asyncOps As Task() = (From addr In Stubs.addrs Select Stubs.SendMailAsync(addr)).ToArray()
Try
Await Task.WhenAll(asyncOps)
Catch exc As Exception
For Each faulted As Task In asyncOps.Where(Function(t) t.IsFaulted)
Console.WriteLine($"Faulted: {faulted.Exception}")
Next
End Try
End Function
Web ããè€æ°ã®ãã¡ã€ã«ãéåæã«ããŠã³ããŒãããäŸãèããŠã¿ãŸãããã ãã®å Žåããã¹ãŠã®éåææäœã«ã¯åçš®ã®çµæã®çš®é¡ããããçµæã«ç°¡åã«ã¢ã¯ã»ã¹ã§ããŸãã
string [] pages = await Task.WhenAll(
from url in urls select DownloadStringTaskAsync(url));
åã® void ãè¿ãã·ããªãªã§èª¬æããã®ãšåãäŸå€åŠçææ³ã䜿çšã§ããŸãã
public static async Task WhenAllDownloadPagesExceptions()
{
Task<string>[] asyncOps =
(from url in Stubs.urls select Stubs.DownloadStringTaskAsync(url)).ToArray();
try
{
string[] pages = await Task.WhenAll(asyncOps);
Console.WriteLine(pages.Length);
}
catch (Exception exc)
{
foreach (Task<string> faulted in asyncOps.Where(t => t.IsFaulted))
{
Console.WriteLine($"Faulted: {faulted.Exception}");
}
}
}
Public Async Function WhenAllDownloadPagesExceptions() As Task
Dim asyncOps As Task(Of String)() =
(From url In Stubs.urls Select Stubs.DownloadStringTaskAsync(url)).ToArray()
Try
Dim pages As String() = Await Task.WhenAll(asyncOps)
Console.WriteLine(pages.Length)
Catch exc As Exception
For Each faulted As Task(Of String) In asyncOps.Where(Function(t) t.IsFaulted)
Console.WriteLine($"Faulted: {faulted.Exception}")
Next
End Try
End Function
Task.WhenAny
WhenAny ã¡ãœããã䜿çšããŠãã¿ã¹ã¯ãšããŠè¡šãããè€æ°ã®éåææäœã®ãããããå®äºãããŸã§éåæçã«åŸ æ©ããŸãã ãã®ã¡ãœããã¯ã次㮠4 ã€ã®äž»èŠãªãŠãŒã¹ ã±ãŒã¹ã«å¯Ÿå¿ããŸãã
åé·æ§: æäœãè€æ°åå®è¡ããæåã«å®äºããæäœãéžæããŸã (ããšãã°ã1 ã€ã®çµæãè¿ãè€æ°ã®æ ªäŸ¡æ å ± Web ãµãŒãã¹ã«é£çµ¡ããæãéãå®äºãããã®ãéžæãããªã©)ã
ã€ã³ã¿ãŒãªãŒã: è€æ°ã®æäœãèµ·åããŠãã®ãã¹ãŠãå®äºããã®ãåŸ æ©ãããããããã®æäœãå®äºæã«åŠçããã
ã¹ããããªã³ã°: ä»ã®æäœãå®äºãããšã远å ã®æäœãéå§ãããããã«èš±å¯ããŸãã ãã®ã·ããªãªã¯ãã€ã³ã¿ãŒãªãŒã ã·ããªãªã®æ¡åŒµæ©èœã§ãã
æ©æã®ææž: ããšãã°ãã¿ã¹ã¯ t1 ã§è¡šãããæäœã¯ãå¥ã®ã¿ã¹ã¯ t2 ãæã€ WhenAny ã¿ã¹ã¯ã«ã°ã«ãŒãåã§ãã WhenAny ã¿ã¹ã¯ãåŸ æ©ã§ããŸãã ã¿ã¹ã¯ t2 ã¯ãã¿ã€ã ã¢ãŠããåãæ¶ãããŸã㯠t1 ãå®äºããåã« WhenAny ã¿ã¹ã¯ãå®äºããåå ãšãªããã®ä»ã®ã·ã°ãã«ã衚ããŠããå¯èœæ§ããããŸãã
åé·æ§
æ ªåŒãè²·ããã©ãããæ±ºå®ããå ŽåãèããŠã¿ãŸãããã ä¿¡é Œã§ããã¹ããã¯ã¬ã³ã¡ã³ããŒã·ã§ã³ Web ãµãŒãã¹ãããã€ãååšããŸãããæ¯æ¥ã®è² è·ã«ãã£ãŠã¯ãåãµãŒãã¹ãç°ãªãæéã«é ããªãå¯èœæ§ããããŸãã WhenAnyã¡ãœããã䜿çšããŠãæäœãå®äºãããšãã«éç¥ãåãåããŸãã
public static async Task WhenAnyRedundancy(string symbol)
{
var recommendations = new List<Task<bool>>()
{
Stubs.GetBuyRecommendation1Async(symbol),
Stubs.GetBuyRecommendation2Async(symbol),
Stubs.GetBuyRecommendation3Async(symbol)
};
Task<bool> recommendation = await Task.WhenAny(recommendations);
if (await recommendation) Stubs.BuyStock(symbol);
}
Public Async Function WhenAnyRedundancy(symbol As String) As Task
Dim recommendations As New List(Of Task(Of Boolean)) From {
Stubs.GetBuyRecommendation1Async(symbol),
Stubs.GetBuyRecommendation2Async(symbol),
Stubs.GetBuyRecommendation3Async(symbol)
}
Dim recommendation As Task(Of Boolean) = Await Task.WhenAny(recommendations)
If Await recommendation Then Stubs.BuyStock(symbol)
End Function
æ£åžžã«å®äºãããã¹ãŠã®ã¿ã¹ã¯ã®ã©ãããããŠããªãçµæãè¿ã WhenAllãšã¯ç°ãªãã WhenAny ã¯å®äºããã¿ã¹ã¯ãè¿ããŸãã ã¿ã¹ã¯ã倱æããå Žåã¯ã倱æããããšãææ¡ããããšãéèŠã§ãããã¿ã¹ã¯ãæåããå Žåã¯ãæ»ãå€ãé¢é£ä»ããããŠããã¿ã¹ã¯ãææ¡ããããšãéèŠã§ãã ãã®ããããã®äŸã«ç€ºãããã«ãè¿ãããã¿ã¹ã¯ã®çµæã«ã¢ã¯ã»ã¹ããããããã«åŸ æ©ããå¿ èŠããããŸãã
WhenAllãšåæ§ã«ãäŸå€ã«å¯Ÿå¿ã§ããå¿
èŠããããŸãã å®äºããã¿ã¹ã¯ãåãåãã®ã§ãé©åã« try/catch ã§å²ãã§ããšã©ãŒãåæ ãããŠè¿ãããã¿ã¹ã¯ãåŸ
æ©ã§ããŸããæ¬¡ã«äŸã瀺ããŸãã
public static async Task WhenAnyRetryOnException(string symbol)
{
Task<bool>[] allRecommendations = new Task<bool>[]
{
Stubs.GetBuyRecommendation1Async(symbol),
Stubs.GetBuyRecommendation2Async(symbol),
Stubs.GetBuyRecommendation3Async(symbol)
};
var remaining = allRecommendations.ToList();
while (remaining.Count > 0)
{
Task<bool> recommendation = await Task.WhenAny(remaining);
try
{
if (await recommendation) Stubs.BuyStock(symbol);
break;
}
catch (WebException)
{
remaining.Remove(recommendation);
}
}
}
Public Async Function WhenAnyRetryOnException(symbol As String) As Task
Dim allRecommendations As Task(Of Boolean)() = {
Stubs.GetBuyRecommendation1Async(symbol),
Stubs.GetBuyRecommendation2Async(symbol),
Stubs.GetBuyRecommendation3Async(symbol)
}
Dim remaining As List(Of Task(Of Boolean)) = allRecommendations.ToList()
While remaining.Count > 0
Dim recommendation As Task(Of Boolean) = Await Task.WhenAny(remaining)
Try
If Await recommendation Then Stubs.BuyStock(symbol)
Exit While
Catch ex As WebException
remaining.Remove(recommendation)
End Try
End While
End Function
ããã«ãæåã®ã¿ã¹ã¯ãæ£åžžã«å®äºããå Žåã§ããåŸç¶ã®ã¿ã¹ã¯ã倱æããå¯èœæ§ããããŸãã ãã®æç¹ã§ãäŸå€ãåŠçããããã®ãªãã·ã§ã³ãããã€ããããŸããèµ·åããããã¹ãŠã®ã¿ã¹ã¯ãå®äºãããŸã§åŸ æ©ã§ããŸãããã®å Žåã¯ã WhenAll ã¡ãœããã䜿çšããããšãããã¹ãŠã®äŸå€ãéèŠã§ããããã°ã«èšé²ããå¿ èŠããããšå€æããããšãã§ããŸãã ãã®ã·ããªãªã§ã¯ãã¿ã¹ã¯ãéåæçã«å®äºãããšãã«ç¶ç¶ã䜿çšããŠéç¥ãåãåãããšãã§ããŸãã
foreach(Task recommendation in recommendations)
{
var ignored = recommendation.ContinueWith(
t => { if (t.IsFaulted) Log(t.Exception); });
}
ãŸãã¯:
foreach(Task recommendation in recommendations)
{
var ignored = recommendation.ContinueWith(
t => Log(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
}
ãŸãã¯ã以äžã®ããã«æå®ã§ããŸãã
private static async void LogCompletionIfFailed(IEnumerable<Task> tasks)
{
foreach (var task in tasks)
{
try { await task; }
catch (Exception exc) { Stubs.Log(exc); }
}
}
Private Async Sub LogCompletionIfFailed(tasks As IEnumerable(Of Task))
For Each task In tasks
Try
Await task
Catch exc As Exception
Stubs.Log(exc)
End Try
Next
End Sub
æåŸã«ãæ®ãã®ãã¹ãŠã®æäœãåãæ¶ãããå ŽåããããŸãã
public static async Task WhenAnyCancelRemainder(string symbol)
{
var cts = new CancellationTokenSource();
var recommendations = new List<Task<bool>>()
{
Stubs.GetBuyRecommendation1Async(symbol, cts.Token),
Stubs.GetBuyRecommendation2Async(symbol, cts.Token),
Stubs.GetBuyRecommendation3Async(symbol, cts.Token)
};
Task<bool> recommendation = await Task.WhenAny(recommendations);
cts.Cancel();
if (await recommendation) Stubs.BuyStock(symbol);
}
Public Async Function WhenAnyCancelRemainder(symbol As String) As Task
Dim cts As New CancellationTokenSource()
Dim recommendations As New List(Of Task(Of Boolean)) From {
Stubs.GetBuyRecommendation1Async(symbol, cts.Token),
Stubs.GetBuyRecommendation2Async(symbol, cts.Token),
Stubs.GetBuyRecommendation3Async(symbol, cts.Token)
}
Dim recommendation As Task(Of Boolean) = Await Task.WhenAny(recommendations)
cts.Cancel()
If Await recommendation Then Stubs.BuyStock(symbol)
End Function
ã€ã³ã¿ãŒãªãŒã
Web ããã€ã¡ãŒãžãããŠã³ããŒãããåã€ã¡ãŒãžãåŠçããå ŽåãèããŠã¿ãŸããã (ããšãã°ãUI ã³ã³ãããŒã«ã«ç»åã远å ãã)ã UI ã¹ã¬ããã§ã€ã¡ãŒãžãé çªã«åŠçããŸãããå¯èœãªéãåæã«ã€ã¡ãŒãžãããŠã³ããŒãããå¿ èŠããããŸãã ãŸããã€ã¡ãŒãžããã¹ãŠããŠã³ããŒãããããŸã§ãUI ãžã®ã€ã¡ãŒãžã®è¿œå ãä¿çããå¿ èŠã¯ãããŸããã 代ããã«ãããããå®äºæã«è¿œå ããããšèããŠããŸãã
public static async Task WhenAnyInterleaving(string[] imageUrls)
{
List<Task<Bitmap>> imageTasks =
(from imageUrl in imageUrls select Stubs.GetBitmapAsync(imageUrl)).ToList();
while (imageTasks.Count > 0)
{
try
{
Task<Bitmap> imageTask = await Task.WhenAny(imageTasks);
imageTasks.Remove(imageTask);
Bitmap image = await imageTask;
Console.WriteLine($"Got image: {image.Width}x{image.Height}");
}
catch { }
}
}
Public Async Function WhenAnyInterleaving(imageUrls As String()) As Task
Dim imageTasks As List(Of Task(Of Bitmap)) =
(From imageUrl In imageUrls Select Stubs.GetBitmapAsync(imageUrl)).ToList()
While imageTasks.Count > 0
Try
Dim imageTask As Task(Of Bitmap) = Await Task.WhenAny(imageTasks)
imageTasks.Remove(imageTask)
Dim image As Bitmap = Await imageTask
Console.WriteLine($"Got image: {image.Width}x{image.Height}")
Catch
End Try
End While
End Function
ããŠã³ããŒãããã€ã¡ãŒãžã® ThreadPool ã«å¯ŸããŠèšç®è² è·ã®é«ãåŠçã䌎ãã·ããªãªã«ã€ã³ã¿ãŒãªãŒããé©çšããããšãã§ããŸããæ¬¡ã«äŸã瀺ããŸãã
public static async Task WhenAnyInterleavingWithProcessing(string[] imageUrls)
{
List<Task<Bitmap>> imageTasks =
(from imageUrl in imageUrls
select Stubs.GetBitmapAsync(imageUrl)
.ContinueWith(t => Stubs.ConvertImage(t.Result))).ToList();
while (imageTasks.Count > 0)
{
try
{
Task<Bitmap> imageTask = await Task.WhenAny(imageTasks);
imageTasks.Remove(imageTask);
Bitmap image = await imageTask;
Console.WriteLine($"Got image: {image.Width}x{image.Height}");
}
catch { }
}
}
Public Async Function WhenAnyInterleavingWithProcessing(imageUrls As String()) As Task
Dim imageTasks As List(Of Task(Of Bitmap)) =
(From imageUrl In imageUrls
Select Stubs.GetBitmapAsync(imageUrl).ContinueWith(Function(t) Stubs.ConvertImage(t.Result))).ToList()
While imageTasks.Count > 0
Try
Dim imageTask As Task(Of Bitmap) = Await Task.WhenAny(imageTasks)
imageTasks.Remove(imageTask)
Dim image As Bitmap = Await imageTask
Console.WriteLine($"Got image: {image.Width}x{image.Height}")
Catch
End Try
End While
End Function
Throttling
ã€ã³ã¿ãŒãªãŒãã®äŸãèããŠã¿ãŸãããã ãããŠãŒã¶ãŒãéåžžã«å€ãã®ç»åãããŠã³ããŒãããŠãããããããŠã³ããŒãé床ãã¹ããããªã³ã°ããªããã°ãªããªããšããç¹ã¯äŸå€ã§ãã ããšãã°ãç¹å®ã®æ°ã®ããŠã³ããŒãã®ã¿ãåæã«å®è¡ããå¿ èŠããããŸãã ãã®ç®æšãéæããã«ã¯ãéåææäœã®ãµãã»ãããéå§ããŸãã æäœãå®äºãããã远å ã®æäœãéå§ããŠä»£ããã«å®è¡ã§ããŸãã
public static async Task WhenAnyThrottling(Uri[] uriList)
{
const int CONCURRENCY_LEVEL = 15;
int nextIndex = 0;
var imageTasks = new List<Task<Bitmap>>();
while (nextIndex < CONCURRENCY_LEVEL && nextIndex < uriList.Length)
{
imageTasks.Add(Stubs.GetBitmapAsync(uriList[nextIndex].ToString()));
nextIndex++;
}
while (imageTasks.Count > 0)
{
try
{
Task<Bitmap> imageTask = await Task.WhenAny(imageTasks);
imageTasks.Remove(imageTask);
Bitmap image = await imageTask;
Console.WriteLine($"Got image: {image.Width}x{image.Height}");
}
catch (Exception exc) { Stubs.Log(exc); }
if (nextIndex < uriList.Length)
{
imageTasks.Add(Stubs.GetBitmapAsync(uriList[nextIndex].ToString()));
nextIndex++;
}
}
}
Public Async Function WhenAnyThrottling(uriList As Uri()) As Task
Const CONCURRENCY_LEVEL As Integer = 15
Dim nextIndex As Integer = 0
Dim imageTasks As New List(Of Task(Of Bitmap))
While nextIndex < CONCURRENCY_LEVEL AndAlso nextIndex < uriList.Length
imageTasks.Add(Stubs.GetBitmapAsync(uriList(nextIndex).ToString()))
nextIndex += 1
End While
While imageTasks.Count > 0
Try
Dim imageTask As Task(Of Bitmap) = Await Task.WhenAny(imageTasks)
imageTasks.Remove(imageTask)
Dim image As Bitmap = Await imageTask
Console.WriteLine($"Got image: {image.Width}x{image.Height}")
Catch exc As Exception
Stubs.Log(exc)
End Try
If nextIndex < uriList.Length Then
imageTasks.Add(Stubs.GetBitmapAsync(uriList(nextIndex).ToString()))
nextIndex += 1
End If
End While
End Function
åæã®ãšã¹ã±ãŒã
ãŠãŒã¶ãŒã®åãæ¶ãèŠæ± (ããšãã°ããŠãŒã¶ãŒããã£ã³ã»ã« ãã¿ã³ãã¯ãªãã¯ããå Žåãªã©) ã«åæã«å¿çããªãããæäœãå®äºãããŸã§éåæçã«åŸ æ©ããŠãããšããŸãã æ¬¡ã®ã³ãŒãã¯ããã®ã·ããªãªã瀺ããŠããŸãã
class EarlyBailoutUI
{
private CancellationTokenSource? m_cts;
public void btnCancel_Click(object sender, EventArgs e)
{
if (m_cts != null) m_cts.Cancel();
}
public async void btnRun_Click(object sender, EventArgs e)
{
m_cts = new CancellationTokenSource();
try
{
Task<Bitmap> imageDownload = Stubs.GetBitmapAsync("url");
await Examples.UntilCompletionOrCancellation(imageDownload, m_cts.Token);
if (imageDownload.IsCompleted)
{
Bitmap image = await imageDownload;
Stubs.Log(image);
}
else imageDownload.ContinueWith(t => Stubs.Log(t));
}
finally { }
}
}
Class EarlyBailoutUI
Private m_cts As CancellationTokenSource
Public Sub btnCancel_Click(sender As Object, e As EventArgs)
If m_cts IsNot Nothing Then m_cts.Cancel()
End Sub
Public Async Sub btnRun_Click(sender As Object, e As EventArgs)
m_cts = New CancellationTokenSource()
Try
Dim imageDownload As Task(Of Bitmap) = Stubs.GetBitmapAsync("url")
Await Examples.UntilCompletionOrCancellation(imageDownload, m_cts.Token)
If imageDownload.IsCompleted Then
Dim image As Bitmap = Await imageDownload
Stubs.Log(image)
Else
imageDownload.ContinueWith(Sub(t) Stubs.Log(t))
End If
Finally
End Try
End Sub
End Class
ãŠãŒã¶ãŒããã£ã³ã»ã«ãéžæãããããã«ããŠãŒã¶ãŒ ã€ã³ã¿ãŒãã§ã€ã¹ã¯å床æå¹ã«ãªããŸãããã ããåºã«ãªãéåææäœã¯åãæ¶ãããŸããã å¥ã®æ¹æ³ãšããŠããã£ã³ã»ã«ããããšæ±ºããå Žåã«ãä¿çäžã®æäœãåãæ¶ãããšãèããããŸãããã ãããã®éããã£ã³ã»ã«èŠæ±ã«ããæäœãæ©æçµäºããå¯èœæ§ãããã®ã§ãæäœãå®äºãããŸã§ã¯ãŠãŒã¶ãŒã€ã³ã¿ãŒãã§ãŒã¹ã埩å ããªãããšãæ€èšãã¹ãã§ãã
class EarlyBailoutWithTokenUI
{
private CancellationTokenSource? m_cts;
public async void btnRun_Click(object sender, EventArgs e)
{
m_cts = new CancellationTokenSource();
try
{
Task<Bitmap> imageDownload = Stubs.GetBitmapAsync("url", m_cts.Token);
await Examples.UntilCompletionOrCancellation(imageDownload, m_cts.Token);
Bitmap image = await imageDownload;
Stubs.Log(image);
}
catch (OperationCanceledException) { }
finally { }
}
}
Class EarlyBailoutWithTokenUI
Private m_cts As CancellationTokenSource
Public Async Sub btnRun_Click(sender As Object, e As EventArgs)
m_cts = New CancellationTokenSource()
Try
Dim imageDownload As Task(Of Bitmap) = Stubs.GetBitmapAsync("url", m_cts.Token)
Await Examples.UntilCompletionOrCancellation(imageDownload, m_cts.Token)
Dim image As Bitmap = Await imageDownload
Stubs.Log(image)
Catch ex As OperationCanceledException
Finally
End Try
End Sub
End Class
æ©æææžã®ãã 1 ã€ã®äŸãšããŠã次ã®ã»ã¯ã·ã§ã³ã§èª¬æããããã«ã WhenAny ã¡ãœããã Delay ã¡ãœãããšçµã¿åãããŠäœ¿çšããå¿ èŠããããŸãã
Task.Delay (ã¿ã¹ã¯ã®é å»¶)
Task.Delay ã¡ãœããã䜿çšããŠãéåæã¡ãœããã®å®è¡ã«äžæåæ¢ã远å ããŸãã ãã®äžæåæ¢ã¯ãããŒãªã³ã° ã«ãŒãã®æ§ç¯ãããŠãŒã¶ãŒå ¥åã®åŠçã®é å»¶ãªã©ãããŸããŸãªçš®é¡ã®æ©èœã«åœ¹ç«ã¡ãŸãã Task.Delay ã¡ãœãããTask.WhenAnyãšå ±ã«äœ¿çšããŠãawaits ã«ã¿ã€ã ã¢ãŠããå®è£ ããããšãã§ããŸãã
å€§èŠæš¡ãªéåææäœ (ããšãã°ãASP.NET Web ãµãŒãã¹) ã®äžéšã§ããã¿ã¹ã¯ã®å®äºã«æéãããããããå Žåã¯ãæäœå šäœãäœäžããå¯èœæ§ããããŸã(ç¹ã«ãå®äºã«å€±æããå Žå)ã ãã®ãããéåææäœãåŸ æ©ããŠãããšãã«ã¿ã€ã ã¢ãŠãã§ããããšãéèŠã§ãã åæ Task.Waitã Task.WaitAllãããã³ Task.WaitAny ã®ã¡ãœããã¯ã¿ã€ã ã¢ãŠãå€ãåãåããŸããã察å¿ãã TaskFactory.ContinueWhenAll/TaskFactory.ContinueWhenAny ãšåè¿°ã® Task.WhenAll/Task.WhenAny ã¡ãœããã§ã¯åãå ¥ããããŸããã 代ããã«ã Task.Delay ãš Task.WhenAny ãäžç·ã«äœ¿çšããŠã¿ã€ã ã¢ãŠããå®è£ ããŸãã
ããšãã°ãUI ã¢ããªã±ãŒã·ã§ã³ã§ãã€ã¡ãŒãžãããŠã³ããŒãããã€ã¡ãŒãžã®ããŠã³ããŒãäžã« UI ãç¡å¹ã«ãããšããŸãã ãã ããããŠã³ããŒãã«æéãããããããå Žåã¯ãUI ãå床æå¹ã«ããŠããŠã³ããŒããç Žæ£ããå¿ èŠããããŸãã
public static async Task<Bitmap?> DownloadWithTimeout(string url)
{
Task<Bitmap> download = Stubs.GetBitmapAsync(url);
if (download == await Task.WhenAny(download, Task.Delay(3000)))
{
return await download;
}
else
{
var ignored = download.ContinueWith(
t => Trace($"Task finally completed: {t.Status}"));
return null;
}
}
static void Trace(string message) => Console.WriteLine(message);
Public Async Function DownloadWithTimeout(url As String) As Task(Of Bitmap)
Dim download As Task(Of Bitmap) = Stubs.GetBitmapAsync(url)
If download Is Await Task.WhenAny(download, Task.Delay(3000)) Then
Return Await download
Else
Dim ignored = download.ContinueWith(Sub(t) TraceMsg($"Task finally completed: {t.Status}"))
Return Nothing
End If
End Function
WhenAllã¯ã¿ã¹ã¯ãè¿ãã®ã§ãåãååãè€æ°ã®ããŠã³ããŒãã«é©çšãããŸãã
public static async Task<Bitmap[]?> DownloadMultipleWithTimeout(string[] imageUrls)
{
Task<Bitmap[]> downloads =
Task.WhenAll(from url in imageUrls select Stubs.GetBitmapAsync(url));
if (downloads == await Task.WhenAny(downloads, Task.Delay(3000)))
{
return await downloads;
}
else
{
downloads.ContinueWith(t => Stubs.Log(t));
return null;
}
}
Public Async Function DownloadMultipleWithTimeout(imageUrls As String()) As Task(Of Bitmap())
Dim downloads As Task(Of Bitmap()) =
Task.WhenAll(From url In imageUrls Select Stubs.GetBitmapAsync(url))
If downloads Is Await Task.WhenAny(downloads, Task.Delay(3000)) Then
Return Await downloads
Else
downloads.ContinueWith(Sub(t) Stubs.Log(t))
Return Nothing
End If
End Function
ã¿ã¹ã¯ ããŒã¹ã®çµã¿åããåã®æ§ç¯
ã¿ã¹ã¯ã¯å®å šã«éåææäœã衚ããæäœãšã®çµåãçµæã®ååŸãªã©ã®åææ©èœãšéåææ©èœãæäŸã§ãããããã¿ã¹ã¯ãæ§æããçµã¿åããåã®äŸ¿å©ãªã©ã€ãã©ãªãæ§ç¯ããŠããã倧ããªãã¿ãŒã³ãæ§ç¯ã§ããŸãã åã®ã»ã¯ã·ã§ã³ã§èª¬æããããã«ã.NET ã«ã¯è€æ°ã®çµã¿èŸŒã¿ã³ã³ãã€ããŒã¿ãå«ãŸããŠããŸãããç¬èªã«ãã«ãããããšãã§ããŸãã æ¬¡ã®ã»ã¯ã·ã§ã³ã§ã¯ãçµã¿åããåã®ã¡ãœãããšåã®äŸãããã€ã瀺ããŸãã
RetryOnFault
å€ãã®å Žåã以åã®è©Šè¡ã倱æããå Žåã«æäœãå詊è¡ããå¿
èŠããããŸãã åæã³ãŒãã®å Žåã¯ã次ã®äŸã® RetryOnFault ãªã©ã®ãã«ã㌠ã¡ãœããããã«ãããŠããã®ã¿ã¹ã¯ãå®è¡ã§ããŸãã
public static T RetryOnFault<T>(Func<T> function, int maxTries)
{
for (int i = 0; i < maxTries; i++)
{
try { return function(); }
catch { if (i == maxTries - 1) throw; }
}
return default(T)!;
}
Public Function RetryOnFaultSync(Of T)(func As Func(Of T), maxTries As Integer) As T
For i As Integer = 0 To maxTries - 1
Try
Return func()
Catch
If i = maxTries - 1 Then Throw
End Try
Next
Return Nothing
End Function
TAP ã䜿çšããŠå®è£ ãããã¿ã¹ã¯ãè¿ãéåææäœçšã«ãã»ãŒåããã«ã㌠ã¡ãœãããæ§ç¯ã§ããŸãã
public static async Task<T> RetryOnFault<T>(Func<Task<T>> function, int maxTries)
{
for (int i = 0; i < maxTries; i++)
{
try { return await function().ConfigureAwait(false); }
catch { if (i == maxTries - 1) throw; }
}
return default(T)!;
}
Public Async Function RetryOnFault(Of T)(func As Func(Of Task(Of T)), maxTries As Integer) As Task(Of T)
For i As Integer = 0 To maxTries - 1
Try
Return Await func().ConfigureAwait(False)
Catch
If i = maxTries - 1 Then Throw
End Try
Next
Return Nothing
End Function
ãã®åŸããã®ã³ã³ãã€ããŒã¿ãŒã䜿çšããŠãå詊è¡ãã¢ããªã±ãŒã·ã§ã³ã®ããžãã¯ã«ãšã³ã³ãŒãã§ããŸãã äŸãã°æ¬¡ãæããããŸãã
// Download the URL, trying up to three times in case of failure
string pageContents = await RetryOnFault(
() => DownloadStringTaskAsync(url), 3);
RetryOnFault颿°ãããã«æ¡åŒµã§ããŸãã ããšãã°ã颿°ã¯å詊è¡ã®éã«åŒã³åºãå¥ã® Func<Task> ãåãå
¥ããŠãæäœãå詊è¡ããã¿ã€ãã³ã°ã決å®ã§ããŸãã äŸãã°æ¬¡ãæããããŸãã
public static async Task<T> RetryOnFaultWithDelay<T>(
Func<Task<T>> function, int maxTries, Func<Task> retryWhen)
{
for (int i = 0; i < maxTries; i++)
{
try { return await function().ConfigureAwait(false); }
catch { if (i == maxTries - 1) throw; }
await retryWhen().ConfigureAwait(false);
}
return default(T)!;
}
Public Async Function RetryOnFaultWithDelay(Of T)(
func As Func(Of Task(Of T)), maxTries As Integer, retryWhen As Func(Of Task)) As Task(Of T)
For i As Integer = 0 To maxTries - 1
Try
Return Await func().ConfigureAwait(False)
Catch
If i = maxTries - 1 Then Throw
End Try
Await retryWhen().ConfigureAwait(False)
Next
Return Nothing
End Function
ãã®åŸã次ã®ããã«é¢æ°ã䜿çšããŠãæäœãå詊è¡ããåã« 1 ç§éåŸ æ©ã§ããŸãã
// Download the URL, trying up to three times in case of failure,
// and delaying for a second between retries
string pageContents = await RetryOnFault(
() => DownloadStringTaskAsync(url), 3, () => Task.Delay(1000));
NeedOnlyOne
å Žåã«ãã£ãŠã¯ãåé·æ§ãå©çšããŠãæäœã®åŸ
æ©æéãšæåã®å¯èœæ§ãåäžãããããšãã§ããŸãã æ ªäŸ¡æ
å ±ãæäŸããè€æ°ã® Web ãµãŒãã¹ãæ€èšããŸããã1 æ¥ã®ããŸããŸãªæé垯ã«ãåãµãŒãã¹ã«ãã£ãŠç°ãªãã¬ãã«ã®å質ãšå¿çæéãæäŸãããå ŽåããããŸãã ãããã®å€åã«å¯ŸåŠããããã«ããã¹ãŠã® Web ãµãŒãã¹ã«èŠæ±ãçºè¡ããå¿çãè¿ããããããã«æ®ãã®èŠæ±ãåãæ¶ããŸãã ãã«ããŒé¢æ°ãå®è£
ãããšãè€æ°ã®æäœãèµ·åããä»»æã®æäœãåŸ
æ©ããŠãããæ®ãã®æäœãåãæ¶ããšããäžè¬çãªãã¿ãŒã³ãç°¡åã«å®è£
ã§ããŸãã æ¬¡ã®äŸã® NeedOnlyOne 颿°ã¯ããã®ã·ããªãªã瀺ããŠããŸãã
public static async Task<T> NeedOnlyOne<T>(
params Func<CancellationToken, Task<T>>[] functions)
{
var cts = new CancellationTokenSource();
var tasks = (from function in functions
select function(cts.Token)).ToArray();
var completed = await Task.WhenAny(tasks).ConfigureAwait(false);
cts.Cancel();
foreach (var task in tasks)
{
var ignored = task.ContinueWith(
t => Stubs.Log(t), TaskContinuationOptions.OnlyOnFaulted);
}
return await completed;
}
Public Async Function NeedOnlyOne(Of T)(
ParamArray functions As Func(Of CancellationToken, Task(Of T))()) As Task(Of T)
Dim cts As New CancellationTokenSource()
Dim tasks As Task(Of T)() = (From func In functions Select func(cts.Token)).ToArray()
Dim completed As Task(Of T) = Await Task.WhenAny(tasks).ConfigureAwait(False)
cts.Cancel()
For Each task In tasks
Dim ignored = task.ContinueWith(
Sub(tsk) Stubs.Log(tsk), TaskContinuationOptions.OnlyOnFaulted)
Next
Return Await completed
End Function
ãã®åŸã次ã®ããã«ãã®é¢æ°ã䜿çšã§ããŸãã
double currentPrice = await NeedOnlyOne(
ct => GetCurrentPriceFromServer1Async("msft", ct),
ct => GetCurrentPriceFromServer2Async("msft", ct),
ct => GetCurrentPriceFromServer3Async("msft", ct));
ã€ã³ã¿ãŒãªãŒããããæäœ
WhenAnyã¡ãœããã䜿çšããŠã€ã³ã¿ãŒãªãŒã ã·ããªãªããµããŒããããšãå€§èŠæš¡ãªã¿ã¹ã¯ ã»ãããæäœãããšãã«ããã©ãŒãã³ã¹ã®åé¡ãçºçããå¯èœæ§ããããŸãã
WhenAny ãåŒã³åºãããã«ãåã¿ã¹ã¯ã®ç¶ç¶åŠçãç»é²ãããŸãã ã¿ã¹ã¯ããN æ°ã®å Žåããã®ããã»ã¹ã¯ãã€ã³ã¿ãŒãªãŒãæäœã®å®è¡æéå
šäœã§ O(N2) ã®ç¶ç¶åŠçãäœæããŸãã 倿°ã®ã¿ã¹ã¯ã»ããã䜿çšããŠããå Žåã¯ãã³ã³ãã€ããŒã¿ (次ã®äŸã®Interleaved ) ã䜿çšããŠããã©ãŒãã³ã¹ã®åé¡ã«å¯ŸåŠããŸãã
public static IEnumerable<Task<T>> Interleaved<T>(IEnumerable<Task<T>> tasks)
{
var inputTasks = tasks.ToList();
var sources = (from _ in Enumerable.Range(0, inputTasks.Count)
select new TaskCompletionSource<T>()).ToList();
int nextTaskIndex = -1;
foreach (var inputTask in inputTasks)
{
inputTask.ContinueWith(completed =>
{
var source = sources[Interlocked.Increment(ref nextTaskIndex)];
if (completed.IsFaulted)
source.TrySetException(completed.Exception!.InnerExceptions);
else if (completed.IsCanceled)
source.TrySetCanceled();
else
source.TrySetResult(completed.Result);
}, CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}
return from source in sources
select source.Task;
}
Public Function Interleaved(Of T)(tasks As IEnumerable(Of Task(Of T))) As IEnumerable(Of Task(Of T))
Dim inputTasks As List(Of Task(Of T)) = tasks.ToList()
Dim sources As List(Of TaskCompletionSource(Of T)) =
(From _i In Enumerable.Range(0, inputTasks.Count) Select New TaskCompletionSource(Of T)()).ToList()
Dim indexRef As Integer() = {-1}
For Each inputTask In inputTasks
inputTask.ContinueWith(Sub(completed)
Dim idx = Interlocked.Increment(indexRef(0))
Dim source = sources(idx)
If completed.IsFaulted Then
source.TrySetException(completed.Exception.InnerExceptions)
ElseIf completed.IsCanceled Then
source.TrySetCanceled()
Else
source.TrySetResult(completed.Result)
End If
End Sub,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default)
Next
Return From source In sources Select source.Task
End Function
ã³ã³ãã€ããŒã¿ãŒã䜿çšããŠãã¿ã¹ã¯ã®å®äºæã«çµæãåŠçããŸãã äŸãã°æ¬¡ãæããããŸãã
IEnumerable<Task<int>> tasks = ...;
foreach(var task in Interleaved(tasks))
{
int result = await task;
âŠ
}
WhenAllOrFirstException
ç¹å®ã®ã¹ãã£ãã¿ãŒ/ã®ã£ã¶ãŒ ã·ããªãªã§ã¯ããã®ãã¡ã® 1 ã€ã«ãšã©ãŒãçºçããªãéããã»ããå
ã®ãã¹ãŠã®ã¿ã¹ã¯ãåŸ
æ©ããå¿
èŠãããå ŽåããããŸãã ãã®å Žåã¯ãäŸå€ãçºçãããããã«åŸ
æ©ã忢ããŸãã ãã®åäœã¯ã次ã®äŸã® WhenAllOrFirstException ãªã©ã®ã³ã³ãã€ããŒã¿ ã¡ãœããã䜿çšããŠå®çŸã§ããŸãã
public static Task<T[]> WhenAllOrFirstException<T>(IEnumerable<Task<T>> tasks)
{
var inputs = tasks.ToList();
var ce = new CountdownEvent(inputs.Count);
var tcs = new TaskCompletionSource<T[]>();
Action<Task> onCompleted = (Task completed) =>
{
if (completed.IsFaulted)
tcs.TrySetException(completed.Exception!.InnerExceptions);
if (ce.Signal() && !tcs.Task.IsCompleted)
tcs.TrySetResult(inputs.Select(t => ((Task<T>)t).Result).ToArray());
};
foreach (var t in inputs) t.ContinueWith(onCompleted);
return tcs.Task;
}
Public Function WhenAllOrFirstException(Of T)(tasks As IEnumerable(Of Task(Of T))) As Task(Of T())
Dim inputs As List(Of Task(Of T)) = tasks.ToList()
Dim ce As New CountdownEvent(inputs.Count)
Dim tcs As New TaskCompletionSource(Of T())()
Dim onCompleted As Action(Of Task) = Sub(completed As Task)
If completed.IsFaulted Then
tcs.TrySetException(completed.Exception.InnerExceptions)
End If
If ce.Signal() AndAlso Not tcs.Task.IsCompleted Then
tcs.TrySetResult(inputs.Select(Function(taskItem) DirectCast(taskItem, Task(Of T)).Result).ToArray())
End If
End Sub
For Each t In inputs
t.ContinueWith(onCompleted)
Next
Return tcs.Task
End Function
ã¿ã¹ã¯ ããŒã¹ã®ããŒã¿æ§é ã®æ§ç¯
ã«ã¹ã¿ã ã¿ã¹ã¯ ããŒã¹ã®çµã¿åããåãæ§ç¯ããæ©èœã«å ããŠãéåææäœã®çµæãšãããã«çµåããããã«å¿ èŠãªåæã®äž¡æ¹ã衚ãããŒã¿æ§é ã Task ãš Task<TResult> ããããšã§ãéåæã·ããªãªã§äœ¿çšããã«ã¹ã¿ã ããŒã¿æ§é ãæ§ç¯ããããã®åŒ·åãªåã«ãªããŸãã
AsyncCache
ã¿ã¹ã¯ã®éèŠãªåŽé¢ã® 1 ã€ã¯ãè€æ°ã®ã³ã³ã·ã¥ãŒããŒã«æž¡ãããšãã§ãããšããããšã§ãã ãã¹ãŠã®ã³ã³ã·ã¥ãŒããŒã¯ããããåŸ æ©ããããç¶ç¶åŠçãç»é²ããããçµæãŸãã¯äŸå€ ( Task<TResult> ã®å Žå) ãååŸãããã§ããŸãã ãã®åŽé¢ã«ããã Task ãš Task<TResult> éåæãã£ãã·ã¥ ã€ã³ãã©ã¹ãã©ã¯ãã£ã§ã®äœ¿çšã«å®å šã«é©ããŠããŸãã Task<TResult>ã«åºã¥ããŠæ§ç¯ããããå°ãããŠåŒ·åãªéåæãã£ãã·ã¥ã®äŸã次ã«ç€ºããŸãã
public class AsyncCache<TKey, TValue> where TKey : notnull
{
private readonly Func<TKey, Task<TValue>> _valueFactory;
private readonly ConcurrentDictionary<TKey, Lazy<Task<TValue>>> _map;
public AsyncCache(Func<TKey, Task<TValue>> valueFactory)
{
if (valueFactory == null) throw new ArgumentNullException(nameof(valueFactory));
_valueFactory = valueFactory;
_map = new ConcurrentDictionary<TKey, Lazy<Task<TValue>>>();
}
public Task<TValue> this[TKey key]
{
get
{
if (key == null) throw new ArgumentNullException(nameof(key));
return _map.GetOrAdd(key, toAdd =>
new Lazy<Task<TValue>>(() => _valueFactory(toAdd))).Value;
}
}
}
Public Class AsyncCache(Of TKey, TValue)
Private ReadOnly _valueFactory As Func(Of TKey, Task(Of TValue))
Private ReadOnly _map As New ConcurrentDictionary(Of TKey, Lazy(Of Task(Of TValue)))()
Public Sub New(valueFactory As Func(Of TKey, Task(Of TValue)))
If valueFactory Is Nothing Then Throw New ArgumentNullException(NameOf(valueFactory))
_valueFactory = valueFactory
End Sub
Default Public ReadOnly Property Item(key As TKey) As Task(Of TValue)
Get
If key Is Nothing Then Throw New ArgumentNullException(NameOf(key))
Return _map.GetOrAdd(key, Function(toAdd) New Lazy(Of Task(Of TValue))(Function() _valueFactory(toAdd))).Value
End Get
End Property
End Class
AsyncCache<TKey,TValue> ã¯ã©ã¹ã¯ãTKeyãåãåããTask<TResult>ãè¿ã颿°ãã³ã³ã¹ãã©ã¯ã¿ãŒãžã®ããªã²ãŒããšããŠåãåããŸãã å
éšãã£ã¯ã·ã§ããªã¯ããã£ãã·ã¥ãã以åã«ã¢ã¯ã»ã¹ããå€ãæ ŒçŽãã AsyncCache ã¯ããã£ãã·ã¥ãåæã«ã¢ã¯ã»ã¹ãããŠããå Žåã§ããããŒããšã« 1 ã€ã®ã¿ã¹ã¯ã®ã¿ãçæããããšãä¿èšŒããŸãã
ããšãã°ãããŠã³ããŒããã Web ããŒãžã®ãã£ãã·ã¥ãäœæã§ããŸãã
private AsyncCache<string,string> m_webPages =
new AsyncCache<string,string>(DownloadStringTaskAsync);
ãã®åŸãWeb ããŒãžã®å
容ãå¿
èŠãªå Žåã¯åžžã«ãéåæã¡ãœããã§ãã®ãã£ãã·ã¥ã䜿çšã§ããŸãã
AsyncCache ã¯ã©ã¹ã䜿çšãããšãã§ããã ãå°ãªãããŒãžãããŠã³ããŒãããçµæããã£ãã·ã¥ã§ããŸãã
static AsyncCache<string, string> m_webPages =
new AsyncCache<string, string>(url => Stubs.DownloadStringTaskAsync(url));
public static async Task UseWebPageCache(string url)
{
string contents = await m_webPages[url];
Console.WriteLine(contents.Length);
}
Private m_webPages As New AsyncCache(Of String, String)(Function(url) Stubs.DownloadStringTaskAsync(url))
Public Async Function UseWebPageCache(url As String) As Task
Dim contents As String = Await m_webPages(url)
Console.WriteLine(contents.Length)
End Function
AsyncProducerConsumerCollection
ãŸããã¿ã¹ã¯ã䜿çšããŠãéåæã¢ã¯ãã£ããã£ã調æŽããããã®ããŒã¿æ§é ãæ§ç¯ããããšãã§ããŸãã åŸæ¥ã®äžŠåèšèšãã¿ãŒã³ã® 1 ã€ã§ãããããã¥ãŒãµãŒ/ã³ã³ã·ã¥ãŒããŒã«ã€ããŠèããŠã¿ãŸãããã ãã®ãã¿ãŒã³ã§ã¯ããããã¥ãŒãµãŒã¯ã³ã³ã·ã¥ãŒããŒã䜿çšããããŒã¿ãçæãããããã¥ãŒãµãŒãšã³ã³ã·ã¥ãŒããŒã¯äžŠåã§å®è¡ã§ããŸãã ããšãã°ãã³ã³ã·ã¥ãŒããŒã¯ã¢ã€ãã 1 ãåŠçããŸããããã¯ãã¢ã€ãã 2 ãçŸåšçç£ããŠãããããã¥ãŒãµãŒã«ãã£ãŠä»¥åã«çæãããŸããã ãããã¥ãŒãµãŒ/ã³ã³ã·ã¥ãŒã㌠ãã¿ãŒã³ã§ã¯ããããã¥ãŒãµãŒã«ãã£ãŠäœæãããäœæ¥ãæ ŒçŽããŠãã³ã³ã·ã¥ãŒããŒã«æ°ããããŒã¿ã®éç¥ãåãåãã䜿çšå¯èœãªå Žåã«ãããèŠã€ããããããã«ãåžžã«ããã€ãã®ããŒã¿æ§é ãå¿ èŠã§ãã
éåæã¡ãœããããããã¥ãŒãµãŒããã³ã³ã³ã·ã¥ãŒããŒãšããŠäœ¿çšã§ããããã«ãããã¿ã¹ã¯ã«åºã¥ããŠæ§ç¯ãããåçŽãªããŒã¿æ§é ãæ¬¡ã«ç€ºããŸãã
public class AsyncProducerConsumerCollection<T>
{
private readonly Queue<T> m_collection = new Queue<T>();
private readonly Queue<TaskCompletionSource<T>> m_waiting =
new Queue<TaskCompletionSource<T>>();
public void Add(T item)
{
TaskCompletionSource<T>? tcs = null;
lock (m_collection)
{
if (m_waiting.Count > 0) tcs = m_waiting.Dequeue();
else m_collection.Enqueue(item);
}
if (tcs != null) tcs.TrySetResult(item);
}
public Task<T> Take()
{
lock (m_collection)
{
if (m_collection.Count > 0)
{
return Task.FromResult(m_collection.Dequeue());
}
else
{
var tcs = new TaskCompletionSource<T>();
m_waiting.Enqueue(tcs);
return tcs.Task;
}
}
}
}
Public Class AsyncProducerConsumerCollection(Of T)
Private ReadOnly m_collection As New Queue(Of T)()
Private ReadOnly m_waiting As New Queue(Of TaskCompletionSource(Of T))()
Public Sub Add(item As T)
Dim tcs As TaskCompletionSource(Of T) = Nothing
SyncLock m_collection
If m_waiting.Count > 0 Then
tcs = m_waiting.Dequeue()
Else
m_collection.Enqueue(item)
End If
End SyncLock
If tcs IsNot Nothing Then tcs.TrySetResult(item)
End Sub
Public Function Take() As Task(Of T)
SyncLock m_collection
If m_collection.Count > 0 Then
Return Task.FromResult(m_collection.Dequeue())
Else
Dim tcs As New TaskCompletionSource(Of T)()
m_waiting.Enqueue(tcs)
Return tcs.Task
End If
End SyncLock
End Function
End Class
ãã®ããŒã¿æ§é ãé 眮ãããšã次ã®ãããªã³ãŒããèšè¿°ã§ããŸãã
static AsyncProducerConsumerCollection<int> m_data = new();
public static async Task ConsumerAsync()
{
while (true)
{
int nextItem = await m_data.Take();
Stubs.ProcessNextItem(nextItem);
}
}
public static void Produce(int data)
{
m_data.Add(data);
}
Private m_data As New AsyncProducerConsumerCollection(Of Integer)()
Public Async Function ConsumerAsync() As Task
While True
Dim nextItem As Integer = Await m_data.Take()
Stubs.ProcessNextItem(nextItem)
End While
End Function
Public Sub Produce(data As Integer)
m_data.Add(data)
End Sub
System.Threading.Tasks.Dataflowåå空éã«ã¯BufferBlock<T>åãå«ãŸããŠããŸããããã¯åæ§ã®æ¹æ³ã§äœ¿çšã§ããŸãããã«ã¹ã¿ã ã³ã¬ã¯ã·ã§ã³åãäœæããå¿ èŠã¯ãããŸããã
static BufferBlock<int> m_dataBlock = new();
public static async Task ConsumerAsyncBlock()
{
while (true)
{
int nextItem = await m_dataBlock.ReceiveAsync();
Stubs.ProcessNextItem(nextItem);
}
}
public static void ProduceBlock(int data)
{
m_dataBlock.Post(data);
}
Private m_dataBlock As New BufferBlock(Of Integer)()
Public Async Function ConsumerAsyncBlock() As Task
While True
Dim nextItem As Integer = Await m_dataBlock.ReceiveAsync()
Stubs.ProcessNextItem(nextItem)
End While
End Function
Public Sub ProduceBlock(data As Integer)
m_dataBlock.Post(data)
End Sub
泚
System.Threading.Tasks.Dataflowåå空éã¯ãNuGet ããã±ãŒãžãšããŠäœ¿çšã§ããŸãã
System.Threading.Tasks.Dataflowåå空éãå«ãã¢ã»ã³ããªãã€ã³ã¹ããŒã«ããã«ã¯ãVisual Studio ã§ãããžã§ã¯ããéãã[ãããžã§ã¯ã] ã¡ãã¥ãŒãã [NuGet ããã±ãŒãžã®ç®¡ç] ãéžæããSystem.Threading.Tasks.Dataflow ããã±ãŒãžããªã³ã©ã€ã³ã§æ€çŽ¢ããŸãã