ã¯ããã«
C#er ã¯å¼å¸ããããã«ä½¿ã£ã¦ãã async/awaitã
ãã㪠async/await ã«ã¤ãã¦ãå æ¥ Stephen Toub æ° (.NET ã®ä¸ã®äººãä¸å¿äººç©ã®ä¸äººã) ã How Async/Await Really Works in C# ã¨ããé常ã«é¢ç½ãè¨äºãæç¨¿ãã¦ãã¾ããã ãã®è¨äºã§ã¯ Stephen æ°ã®è¨äºããã¼ã¹ã«ãC# ã«ãã㦠async/await ã¯å®éã©ããã£ã¦åãã¦ãã®ï¼ã¨ããã話ããã¦ããã¾ãã 以åã« C#ã§ã®éåæã¡ã½ããã®åæã ã¨ãã翻訳è¨äºãæ¸ããã®ã§ãããå ã«ãªã£ãè¨äºã 2017/11 (.NET Core 2 ãªãªã¼ã¹ç´å¾) ã«æ¸ããããã®ã§ã2023/03 æç¹ (.NET 7 ãªãªã¼ã¹å¾) ã®å®è£ ã¨ã¯ã ãã¶ç°ãªã£ã¦ãã¾ãã(å§åçã«ããã©ã¼ãã³ã¹æ¹å/æé©åããªããã¦ãã¾ãã)ã
ã¡ãªã¿ã«ãasync/await é¢ä¿ãªããã«ãã¹ã¬ããé¢é£ã«ã¤ãã¦ã¯ã以å ãC#ããã«ãã¹ã¬ããé¢é£æä½ã®è©³èª¬ã ã¨ããè¨äºãæ¸ããã®ã§ãæ°ã«ãªãæ¹ã¯æ¯éèªãã§ã¿ã¦ãã ããã
ãªããè¨äºä¸ã§ã¯ .NET Core ã¨è¨è¿°ããå ´åã.NET (5,6,7) ãå«ãäºã¨ãã¾ãã .NET Framework 㨠.NET ã§å¼ã³åããã¨ã¡ãã£ã¨åããã¥ããã®ã§ã.NET Framework 㨠.NET Core ã§å¼ã³åãã¾ãã
ã¾ãè¨äºä¸ã«æ¸ãã¦ããã³ã¼ãã¯çè§£ã®ãããããåªå ãã¦ãããããããçãã¦ãããããããå®éã® dotnet/runtime ã®ã³ã¼ãã¨ã¯ç°ãªãå ´åãããã¾ãã
ç»å£ç
.NET ã©ã 2023/05/27 ã§çºè¡¨ããè³æã§ãã 30 åç»å£çãªã®ã§ããã®è¨äºããã¯ã¢ããµãªç®ã«çºã¾ã£ã¦ãã¾ãã
ããã¦ä»¥ä¸ãç»å£æã®é²ç»ã§ã(åçä½ç½®ã¯åããã¦ã¾ã)ã ã¡ãªã¿ã«ãçµæ§ãªé度ã§åã£ã¦ãã¾ãã (èªè¦ã¯ãã¦ã¾ã) ãå ¨ä½ã®é°å²æ°ããªãã¨ãªã 30 åã§ç¥ã£ã¦ãããã ã¨ããæãã®ã¤ããã§åã£ã¦ããã®ã§ãã¾ããã®ã¤ããã§èãã¦ããã ãã®ããããã¨æãã¾ãã 大äºãªã®ã¯é°å²æ°ã§ããé°å²æ°ã ã¡ããã¨çè§£ããã®ã« 30 åã¯è¶³ããªããããã®ã§ãã¾ããããªå²ãåããã ãã㦠30 åã§ãªãã¨ãªãé°å²æ°ãã¤ããã å¾ãç´°ããã¨ãããæ°ã«ãªã£ããã¹ã©ã¤ããç¨ããããã®ããã°ã®è¨äºã®è©²å½ç®æãèªãã§ããã ãã®ãããããªã¼ã¨æãã¾ãã
Taskã®æ¬è³ª
ããããåºæ¬çãªè©±ããå
¥ãã¾ããããããã Task
ã£ã¦ãªãã§ãããï¼
ã¾ã㯠async/await æãã«ãã¦è¦ã¦ããã¾ãã
Task
ã¨ã¯ãè¨ã£ã¦ãã¾ãã°ãå®äºã®ã·ã°ãã«ã®åä¿¡ã¨è¨å®ã調æ´ããããã®ãã¼ã¿æ§é ãã«ããã¾ãããå®éã® Task
ã¯è¤éã§ããã大äºãªã¨ããã ãæãåºãã¦åç´åãã¦ãã¾ãã°ã以ä¸ã® MyTask
ç¨åº¦ã«ãªãã¾ãã
class MyTask { // ã¿ã¹ã¯ãå®äºãããã©ãããç¥ãããã®ãã£ã¼ã«ã private bool _completed; // ã¿ã¹ã¯ã失æããåå ã¨ãªã£ãã¨ã©ã¼ãä¿åããããã®ãã£ã¼ã«ã private Exception? _error; // Task å®äºå¾ã«å¼ã³åºãããããªã²ã¼ã private Action<MyTask>? _continuation; // å®è¡ã³ã³ããã¹ãã詳細ã¯å¾è¿° private ExecutionContext? _ec; ... }
MyTask
ãå¦çã®çµæãæã¤å ´åãMyTask<TResult>
ã¨ãªããTResult _result
ã¨ãããã£ã¼ã«ããå¿
è¦ã«ãªãããããã¾ããããã¨ããããçµæã¯æããªãæ¹åã§ã
ãã¦ãMyTask
å®äºå¾ã® continuation (ç¶ç¶å¦ç) ã®ã³ã¼ã«ããã¯ãç»é²ããããã®ã¡ã½ãã(ContinueWith
)ãå¿
è¦ã§ãã
class MyTask { // çç¥ public void ContinueWith(Action<MyTask> action) { lock (this) { if (_completed) { ThreadPool.QueueUserWorkItem(_ => action(this)); } else if (_continuation is not null) { throw new InvalidOperationException("Unlike Task, this implementation only supports a single continuation."); } else { _continuation = action; _ec = ExecutionContext.Capture(); } } } }
ããã«ãå¦çãå®äºããäºãéç¥ããããã以ä¸ã®ãããªã¡ã½ãããå®è£ ããå¿ è¦ãããã¾ãã
class MyTask { // çç¥ public void SetResult() => Complete(null); public void SetException(Exception error) => Complete(error); private void Complete(Exception? error) { lock (this) { if (_completed) { throw new InvalidOperationException("Already completed"); } _error = error; _completed = true; if (_continuation is not null) { ThreadPool.QueueUserWorkItem(_ => { if (_ec is not null) { ExecutionContext.Run(_ec, _ => _continuation(this), null); } else { _continuation(this); } }); } } } }
æå¾ã«ãtask ã§çºçããä¾å¤ã伿ããæ¹æ³ãå¿
è¦ã§ã(ãã MyTask<TResult>
ã§ããã°ãä¾å¤ã ãã§ãªããTResult _result
ãè¿ããå¿
è¦ãããã¾ã)ã以ä¸ã®ããã«ãWait
ã¡ã½ãããå®è£
ãããã¨ã§ãtask ã®çµäºã¾ã§å¾
æ©(ãããã¯)ãããã task å
ã®å¦çã§ä¾å¤ãèµ·ãããããã伿ãããäºãã§ããããã«ãªãã¾ãã
class MyTask { // çç¥ public void Wait() { ManualResetEventSlim? mres = null; lock (this) { if (!_completed) { mres = new ManualResetEventSlim(); ContinueWith(_ => mres.Set()); } } mres?.Wait(); if (_error is not null) { ExceptionDispatchInfo.Throw(_error); } } }
åç´åãã¦ãã¾ãã°ãæ¬è³ªã¯ããã ãã§ãï¼
ããããcontinuation (ç¶ç¶å¦ç) ã¯æªã ã³ã¼ã«ããã¯ã§ãã async control flow ãã¨ã³ã³ã¼ãããããã«ãã³ã¼ã«ããã¯ã使ãäºãå¼·å¶ããããã¦ãã¾ãã è±ã³ã¼ã«ããã¯ããåæçãªæ¸ãå¿å°ã§éåæã©ãããããããã§ãããï¼
ãªããä»åä¾ã¨ãã¦æ¸ãã
MyTask
ã§ãããããã«ã¯SetResult/SetException
ãç¨æããã¦ãã¾ãã 䏿¹ã§ãå®éã®Task
(System.Threading.Tasks.Task) ã«ã¯SetResult/SetException
ã¯åå¨ãã¾ããã
Task
ã«ã¯TrySetResult/TrySetException
ã¨ãã£ãã¡ã½ãããããã¾ããããã㯠public ã«ã¯ãªã£ã¦ããªããinternal ãªã¡ã½ããã§ããSetResult/SetException
ã使ã£ã¦è¡ããããªäºã¯TaskCompletionSource
ãç¨ãã¦å®è£ ããå¿ è¦ãããã¾ããTaskCompletionSource
ã¯Task
ãªãã¸ã§ã¯ãã®ä½æã¨ãã®å®äºã® producer ã¨ãã¦æ©è½ãã¦ãã¾ãã ããã¯æè¡çã«å¿ è¦ã ããã§ã¯ãªããtask ããå®äºã·ã°ãã«ãéä¿¡ã§ããªãããã«ããããã§ãã å®äºã·ã°ãã«ã¯ãtask ã使ããå´ã®å®è£ ã®è©³ç´°ã§ãããtask ãæ¶è²»ãã¦ããå´ã¯ç¥ãå¿ è¦ãããã¾ããããå®äºã·ã°ãã«ãéããã¹ãã§ã¯ããã¾ããã ãªã®ã§ãtask 使å´ã¯TaskCompletionSource
ã表ã«åºããªãããã«äºã§ãtask ã®å®äºã®æ¨©ãä»äººã«æ¡ãããªãããã«ããäºãã§ããã®ã§ãã
C# ã®ã¤ãã¬ã¼ã¿
çªç¶ã®ã¤ãã¬ã¼ã¿ã«ããã¤ãã¬ã¼ã¿ã£ã¦IEnumerable<T>
ï¼ä»ã¾ã§éåæå¦çã®è©±ããã¦ããã®ã«ãçªç¶ã®ä½ï¼ï¼ãã¨å°æããããããã¾ããããè±ã³ã¼ã«ããã¯ãå®ç¾ããã«ã¯ C# ã®ã¤ãã¬ã¼ã¿ã«è§£æ±ºã®ç³¸å£ãããã¾ãã
C# ã®ã¤ãã¬ã¼ã¿ã¯ C# 2.0 ã§å°å
¥ããã以ä¸ã®ãããªæãã§æ¸ããããã«ãªãã¾ããã
public static IEnumerable<int> Fib() { int prev = 0, next = 1; yield return prev; yield return next; while (true) { int sum = prev + next; yield return sum; prev = next; next = sum; } }
ããã使ãå´ã¯ããããããªæ¸ãæ¹ãã§ãã¾ãã
foreach (int i in Fib()) { if (i > 100) break; Console.Write($"{i} "); } foreach (int i in Fib().Take(12)) { Console.Write($"{i} "); } using IEnumerator<int> e = Fib().GetEnumerator(); while (e.MoveNext()) { int i = e.Current; if (i > 100) break; Console.Write($"{i} "); }
ããã«é¢ãã¦é¢ç½ãäºã¯ãä¸è¨ã®ãããªæ¸ãæ¹ãå®ç¾ããããã«ã¯ãFib ã¡ã½ããã«å
¥ã£ããåºããã§ããããã«ããå¿
è¦ããããã¨ããäºã§ãã
ã©ãããäºãã¨ããã¨ãã¾ã MoveNext
ãå¼ã³åºã㨠Fib ã¡ã½ããã«å
¥ããã¡ã½ãã㯠yield return
ã«ééããã¾ã§å®è¡ããã¾ãã
ãã㦠yield return
ã«ééããæç¹ã§ MoveNext
ã®å¼ã³åºã㯠true ãè¿ãããã®å¾ã® Current
㯠yield return
ãããå¤ãè¿ãå¿
è¦ãããã¾ãã
ããã¦åã³ MoveNext
ãå¼ã³åºãã¨ãåå yield return
ãããè¡ã®ç´å¾ããå®è¡ãããå¿
è¦ãããã¾ãã
ã¤ã¾ããååå¼ã³åºããã¨ãã®ç¶æ
ããã®ã¾ã¾ã«ãã¦ããå¿
è¦ãããã¾ãã
ãã®ãããC# ã®ã¤ãã¬ã¼ã¿ã¯äºå®ä¸ã®ã³ã«ã¼ãã³ã§ãããã³ã³ãã¤ã©ã¯ããããã¹ãã¼ããã·ã³ã¨ãã¦å±éãã¾ãã
ãã¨ãã°ãä¸è¨ã® Fib ã¡ã½ããã¯ä»¥ä¸ã®ããã«å±éããã¾ãã
public static IEnumerable<int> Fib() => new <Fib>d__0(-2); [CompilerGenerated] private sealed class <Fib>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable { private int <>1__state; private int <>2__current; private int <>l__initialThreadId; private int <prev>5__2; private int <next>5__3; private int <sum>5__4; int IEnumerator<int>.Current => <>2__current; object IEnumerator.Current => <>2__current; public <Fib>d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <prev>5__2 = 0; <next>5__3 = 1; <>2__current = <prev>5__2; <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = <next>5__3; <>1__state = 2; return true; case 2: <>1__state = -1; break; case 3: <>1__state = -1; <prev>5__2 = <next>5__3; <next>5__3 = <sum>5__4; break; } <sum>5__4 = <prev>5__2 + <next>5__3; <>2__current = <sum>5__4; <>1__state = 3; return true; } IEnumerator<int> IEnumerable<int>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <Fib>d__0(0); } IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<int>)this).GetEnumerator(); void IEnumerator.Reset() => throw new NotSupportedException(); void IDisposable.Dispose() { } }
ã¡ãªã¿ã«ãC# ã§ã¯
<>1__state
ã¿ãããª<>
ã¨ãã®è¨å·ãå«ããã£ã¼ã«ãåã¯ã¤ãããã¾ããããIL èªä½ã«ã¯ãã®ãããã®å¶éããªãã®ã§ã³ã³ãã¤ã©ãçæããåããã£ã¼ã«ãåã«ã¯<>
ããã使ããã¦ãã¾ãã
ã¹ãã¼ããã·ã³èªä½ã«ã¯ãã¹ãã¼ããã·ã³ã¨ãã¦ç¶æ
ãã©ãã¾ã§é²ãã ããä¿åããããã®ãã£ã¼ã«ã(ããã§ã¯ <>1__state
) ãçæããã¦ãã¾ãã
ãã㦠Fib ã¡ã½ããã®ãã¸ãã¯ã¯ãã¹ã¦ MoveNext
ã¡ã½ããã®ä¸ã«ãããã¾ã MoveNext
ã«ã¯æå¾ã«ãµã¹ãã³ãããå ´æã«ã¸ã£ã³ãããããã®åå²ãªã©ã追å ããã¦ãã¾ãã
ã¾ã Fib ã¡ã½ããå
ã®ãã¼ã«ã«å¤æ°ã¨ãã¦æ¸ãã prev
/next
/sum
ãªã©ã®å¤æ°ã¯ãMoveNext
ã®å¼ã³åºãã«ã¾ããã£ã¦æç¶ããããã«ãã¹ãã¼ããã·ã³ã®ãã£ã¼ã«ãã¨åãã¦ãã¾ãã
ãã¦ãå
ã»ã© Fib ã¡ã½ããã foreach ã§ã¯ãªããæåã§ MoveNext
ãå©ãã¦ãé©åãªã¿ã¤ãã³ã°ã§ã¤ãã¬ã¼ã¿ãé²ã¾ãããã¨ãåºæ¥ãäºã示ãã¾ããã
// åæ² using IEnumerator<int> e = Fib().GetEnumerator(); while (e.MoveNext()) { int i = e.Current; if (i > 100) break; Console.Write($"{i} "); }
ããã§ããããéåæå¦çãå®äºããã¨ãå®è¡ããã continuation ã¨ãã¦ã¤ãã¬ã¼ã¿ã® MoveNext
ãå¼ã³åºããã¨ããããã©ãã§ããããï¼
ã¤ã¾ãããããyield return
ã§éåæå¦çã表ã task ãè¿ããconsumer å´ã®ã³ã¼ãããã® yield return
ããã task ã« continuation ãããã¯ãã¦ã
ãã® continuation ã ã¤ãã¬ã¼ã¿ã® MoveNext
ãå®è¡ãã¦ã¤ãã¬ã¼ã¿ãé²ããã¨ãããã©ãã§ãããï¼
ãã®ãããªã¢ããã¼ãã§ã¯ã次ã®ãããªãã«ãã¼ã¡ã½ãããæ¸ããã¨ãã§ãã¾ãã
static Task IterateAsync(IEnumerable<Task> tasks) { var tcs = new TaskCompletionSource(); IEnumerator<Task> e = tasks.GetEnumerator(); void Process() { try { if (e.MoveNext()) { e.Current.ContinueWith(t => Process()); return; } } catch (Exception e) { tcs.SetException(e); return; } tcs.SetResult(); }; Process(); return tcs.Task; }
task ã®ã¤ãã¬ã¼ã¿ããã©ã¡ã¼ã¿ã¨ãã¦æ¸¡ããããã® Current
ãè¿ãã task ã« continuation ãç»é²ãã(ContinueWith(t => Process())
) äºã§ããã® task ãå®äºãããã¤ãã¬ã¼ã¿ã® MoveNext
ãå©ããã¾ããããã¦æ¬¡ã®æ°ãã task ãåå¾ããããã«å¯¾ãã¦å度 continuation ç»é²ãããããå®äºãããã¾ã MoveNext
ãå©ãã...ã¨ããã®ãç¹°ãè¿ãæãã§ãã
ãã端çã«èª¬æããã¨ãã¤ãã¬ã¼ã¿ã® Current
ã§åå¾ãã task ãå®äºãããã弿°ã§æ¸¡ãããã¤ãã¬ã¼ã¿ã® MoveNext()
ãå¼ã³åºãããããã«ããããã®ã¡ã½ããã§ãã
å ·ä½çã«ã©ã使ãããã¿ãã¨ãä½ãå¬ããããä¸ç®çç¶ã§ãã
static Task CopyStreamToStreamAsync(Stream source, Stream destination) { // ä¸è¨ã® IterateAsync ã使ã return IterateAsync(Impl(source, destination)); static IEnumerable<Task> Impl(Stream source, Stream destination) { var buffer = new byte[0x1000]; while (true) { Task<int> read = source.ReadAsync(buffer, 0, buffer.Length); yield return read; int numRead = read.Result; if (numRead <= 0) { break; } Task write = destination.WriteAsync(buffer, 0, numRead); yield return write; write.Wait(); } } }
ã¾ããImpl
颿°ã§è¿ãããã¤ãã¬ã¼ã¿(IEnumerable<Task>
) ã IterateAsync
ã MoveNext
ããäºã§é²ããCurrent
(ä¸çªæåã®å®è¡ã§ããã°ãsource.ReadAsync
ã®è¿ãå¤ã§ãã Task
ãªãã¸ã§ã¯ã) ãåå¾ãã¾ãã
ãã® Current
ã«å¯¾ãã¦ãã¤ãã¬ã¼ã¿ãé²ããããã® continuation ã¨ãã¦ãt => Process()
ãç»é²ããäºã§ãCurrent
ã§åå¾ãã task ãå®äºã次第ãã¤ãã¬ã¼ã¿ã MoveNext
ã§åã«é²ããã¨ã«ãã£ã¦ destination.WriteAsync
ã¾ã§ãå®è¡ããã¾ãã
ãã㦠destination.WriteAsync
ã®è¿ãå¤ã次㮠Current
ã¨ã㦠IterateAsync
ã®å
ã® Process
ã§åå¾ãããã¾ãã¤ãã¬ã¼ã¿ãé²ããããã® continuation ã¨ãã¦ãt => Process()
ããã® task ã«ç»é²ãã...ã¨ããæãã§ãã
ã¨ããããã§ãCopyStreamToStreamAsync
ã«ããã¦ãè±ã³ã¼ã«ããã¯ãåºæ¥ã¦ãã¾ãããããå¬ããã
ãã㦠CopyStreamToStreamAsync
ã® yield return
ã await
ã«ç½®ãæããã¤ããã§ãã¼ã£ã¨ç¨ãã§ã¿ã¦ãã ããã
await ã使ã£ãå ´åã¨ã»ã¼åãã§ããäºã«æ°ä»ãã¯ãã§ãã
ããã C# ã¨.NETã«ããã async/await ã®å§ã¾ãã§ãã
C# ã³ã³ãã¤ã©ã®ã¤ãã¬ã¼ã¿ã¨ async/await ããµãã¼ããããã¸ãã¯ã®ç´95%ã¯å
±æããã¦ãã¾ãã
ç°ãªãæ§æãç°ãªãåãé¢ä¿ãã¾ãããåºæ¬çã«ã¯åã夿ãè¡ãã¾ãã
async/await ãç»å ´ããåãã¤ã¾ã C# 5.0 ããªãªã¼ã¹ãããããåã®æä»£ã§ã¯ãå®éãã®ãããªæ¸ãæ¹ããã¦ããéçºè ãããããã§ãã
async/await
ãããã async/await ã®ã話ã§ãï¼ å®éã«ãasync/await ãã©ã®ããã«åä½ããã®ããè¦ã¦ããã¾ãããã
ã¾ãåæã®ã¡ã½ããã§ãã CopyStreamToStream
ã¯ãããªæãã§ã
public void CopyStreamToStream(Stream source, Stream destination) { var buffer = new byte[0x1000]; int numRead; while ((numRead = source.Read(buffer, 0, buffer.Length)) != 0) { destination.Write(buffer, 0, numRead); } }
éåæã§ãã CopyStreamToStreamAsync
ã¯ãããªæãã
public async Task CopyStreamToStreamAsync(Stream source, Stream destination) { var buffer = new byte[0x1000]; int numRead; while ((numRead = await source.ReadAsync(buffer, 0, buffer.Length)) != 0) { await destination.WriteAsync(buffer, 0, numRead); } }
åæçããéåæçã¸ã®å¤æ´ç¹ã¯ä»¥ä¸ã®ããã ãã§ãã
- ã·ã°ããã£ã
void
ããasync Task
ã«å¤ãã Read/Write
ã®ä»£ããã«ReadAsync/WriteAsync
ãããããå¼ã³ããã®ä¸¡æ¹ã®æä½ã«await
ãä»ãã
éåæã«ããããã®ãã®ä»è«¸ã ã®ä½æ¥ã¯å ¨ã¦ã³ã³ãã¤ã©ã¨ã³ã¢ã©ã¤ãã©ãªãè¡ããã³ã¼ããå®éã«ã©ã®ããã«å®è¡ãããããæ ¹æ¬çã«å¤ãã¾ãã ããã§ã¯ããããã©ã®ãããªãã®ããæãä¸ãã¦ã¿ã¾ãããã
Compiler Transform
å
ã»ã©ã® CopyStreamToStreamAsync
ã¯ãã³ã³ãã¤ã©ã«ãã£ã¦ä»¥ä¸ã®ããã«å±éããã¾ãã
[AsyncStateMachine(typeof(<CopyStreamToStreamAsync>d__0))] public Task CopyStreamToStreamAsync(Stream source, Stream destination) { // ã¹ãã¼ããã·ã³ (struct) ã®åæå <CopyStreamToStreamAsync>d__0 stateMachine = default; // ã¹ãã¼ããã·ã³ ã«å¿ è¦ãªãã©ã¡ã¼ã¿ãä¿åãã¦ãã // ã¾ã㯠Builder (struct) stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create(); // ãã©ã¡ã¼ã¿ãªã©ãã¹ãã¼ããã·ã³ã«æ ¼ç´ stateMachine.source = source; // stateMachine.destination = destination; // åæç¶æ 㯠-1 stateMachine.<>1__state = -1; // ã¹ãã¼ããã·ã³ãéå§ stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } private struct <CopyStreamToStreamAsync>d__0 : IAsyncStateMachine { public int <>1__state; public AsyncTaskMethodBuilder <>t__builder; public Stream source; public Stream destination; // CopyStreamToStreamAsync å ã®ãã¼ã«ã«å¤æ°ã¯ããã«ã private byte[] <buffer>5__2; private TaskAwaiter <>u__1; private TaskAwaiter<int> <>u__2; ... }
ããã§éè¦ãªã®ã¯ãasync ã¡ã½ãããåæçã«çµäºããå ´åãzero allocation ã§ããã¨ããäºã§ãã
ã³ã³ãã¤ã©ã«ãã£ã¦çæãããã¹ãã¼ããã·ã³ã¯ struct ã§ãããAsyncTaskMethodBuilder
ã struct ã§ããããã
ã¾ããã³ã³ãã¤ã©ãåºåãããã®ã¹ãã¼ããã·ã³ã¯éçºè
ãè¨è¿°ãããã¸ãã¯ãå
¨ã¦å¤æããç©ã ãã§ãªãããã®ã¡ã½ããã®ç¾å¨ã®é²è¡ç¶æ
ãä¿åããããã®ãã£ã¼ã«ã(ãã®ä¾ã§ã¯ int <>1__state
) ãããã¼ã«ã«å¤æ° (ããã§ã¯ byte[] <buffer>5__2
) ãªã©ãçæããã¦ãã¾ãã
C# ã®ã¤ãã¬ã¼ã¿(IEnumerable <T>
) ã§çæãããã¹ãã¼ããã·ã³ã¨è¦é ã¯ã»ã¼åãã§ããï¼
ã¡ãªã¿ã«ãã®ã¹ãã¼ããã·ã³ã¯ debug build ã§ã¯ struct ã§ã¯ãªããclass ã§å®ç¾©ãããã®ã§ãçæãããã³ã¼ããçºããå ´åã¯æ°ãä»ãã¦ãã ããã
CopyStreamToStreamAsync
å
ã§ã¹ãã¼ããã·ã³(<CopyStreamToStreamAsync>d__0 stateMachine
)ã default
ã§åæåããå¾ãAsyncTaskMethodBuilder.Create()
ãå¼ã°ãã¦ããäºãåããã¾ãã
AsyncMethodBuilder (AsyncTaskMethodBuilder
ã®ä»ã«ããAsyncValueTaskMethodBuilder
ã PoolingAsyncValueTaskMethodBuilder
çãåå¨ãã¾ãã以å¾ããã«ãã¼ãã¨æ¸ãã¦ãã£ãããããã®ãã㪠AsyncMethodBuilder ãæãã¾ã) ã®å½¹å²ã¯ãTask
ããããã£ã¨ãã¦é²åºãã Task
ãValueTask
çã®åã®ãªãã¸ã§ã¯ãã使ãã
SetResult
/SetException
ãç¨ãã¦é©åãªçµæãããã¯ä¾å¤ããã®ãªãã¸ã§ã¯ãã«è¨å®ãããã¨ã§å®äºç¶æ
ã«ãã
ã¾ã å®äºãã¦ããªãå¾
æ©ç¶æ
ã®ãªãã¸ã§ã¯ãã«å¯¾ãã¦ã¯ continuation ã®ç»é²ãªã©ã®å¦çãè¡ã£ããããäºã§ã(AwaitOnCompleted
/AwaitUnsafeOnCompleted
çãç¨ãã)ã
ãããèéã«é¸ãã¾ãã CopyStreamToStreamAsync
ã®è¿ãå¤ã®å㯠Task
ã§ããã«ãã¼ã¨ã㦠AsyncTaskMethodBuilder
ã使ããã¦ãã¾ããã
å®ã¯ãããä¸çªæåã®ä¾ã§åºãã¦ãããã㪠MyTask
ãªã©ã®èªåã§å®ç¾©ãã Task
ã£ã½ãå (Task-like) ã«å¯¾ãã¦ãç¬èªã«ãã«ãã¼ãå®ç¾©ãã¦ããããã¨ãã§ãã
ã³ã³ãã¤ã©ã async ã¡ã½ãããå±éããæã«ãã®ç¬èªã®ãã«ãã¼ã使ãããã«ãããããäºãåºæ¥ãããã¾ãã
ããã¯ã©ããããã¨ããã¨ãTask-like ãªåã«ãããã¦ã[AsyncMethodBuilder(typeof(T))]
屿§ã§é©åãªãã«ãã¼ãç´ã¥ãã¦ä¸ããã ãã§ãã
ãã¨ãã°ä»¥ä¸ã®ããã«[AsyncMethodBuilder(typeof(MyTaskMethodBuilder))]
ã¨ããäºã§ãasync Task CopyStreamToStreamAsync(...)
ã§ã¯ãªãasync MyTask CopyStreamToStreamAsync(...)
ã¨ãã£ãã¡ã½ãããå®ç¾©ããéã«ãAsyncTaskMethodBuilder
ã§ã¯ãªããMyTaskMethodBuilder
ã使ç¨ãããããã«ãªã£ãããã¾ãã
[AsyncMethodBuilder(typeof(MyTaskMethodBuilder))] // <- èªåã§å®ç¾©ãããã«ãã¼ãæå® public class MyTask { ... } // ãã«ãã¼ã«å¿ è¦ãªå®ç¾©ãé å¼µã£ã¦èªåã§å®è£ ãã public struct MyTaskMethodBuilder { public static MyTaskMethodBuilder Create() { ... } public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { ... } public void SetStateMachine(IAsyncStateMachine stateMachine) { ... } public void SetResult() { ... } public void SetException(Exception exception) { ... } public void AwaitOnCompleted<TAwaiter, TStateMachine>( ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { ... } public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>( ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { ... } public MyTask Task { get { ... } } }
ã¨ã¯ããããã®æ©è½ã¯æ®ã© ValueTask
ã®ããã«(C# 7.0 ãã)å°å
¥ããããã®ãªã®ã§ãæ®éã®éçºè
ããã«ãã¼ãèªåã§å®è£
ãããããäºã¯ã¾ãç¡ããããã¹ãã§ããªãã¨æã£ã¦ããã¦ãã ããã
ã¡ãªã¿ã«ãæ®éã® Task
ã¯ç¹å¥æ±ãããã¦ããã®ã§ã[AsyncMethodBuilder(typeof(AsyncTaskMethodBuilder))]
ã¨ãã¯ä»ãã¦ãã¾ããã
ã¾ãã[AsyncMethodBuilder]
ã¯åã«å¯¾ãã¦ã¢ããã¼ã·ã§ã³ããã ããããªãã¦ãã¡ã½ããåä½ã§ãã¢ããã¼ã·ã§ã³ããäºãã§ãã¾ãã
å
ã»ã©ããè¦ã¦ããéããasync ã¡ã½ããã¯ã³ã³ãã¤ã©ã«ãã£ã¦ã¹ãã¼ããã·ã³ã¨ãã¦å±éãããã¡ã½ããæ¬ä½ã¯ã¹ãã¼ããã·ã³ã¨ãã«ãã¼ã®ä½æã¨åæåããªããããããããã«å±éãããããã§ããã
ãã®éã«ä½¿ããããã«ãã¼ãã«ã¹ã¿ã ã§ãã¾ãã
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; internal sealed class Http2MessageBody : MessageBody { // ~~ çç¥ ~~ // 以ä¸ã®ããã«ãã¡ã½ããã¬ãã«ã§ [AsyncMethodBuilder] ãé©ç¨ããäºãå¯è½ã // ãã®ã¡ã½ãããã³ã³ãã¤ã©ãå±éããæã // æå®ãããã«ãã¼ã使ãããããã«ãªãã // PoolingAsyncValueTaskMethodBuilder ã«ã¤ãã¦ã¯å¾è¿° [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] public override async ValueTask<ReadResult> ReadAsync(CancellationToken cancellationToken = default) { // ~~ çç¥ ~~ } }
èéãã帰ã£ã¦ãã¦ãæ¬é¡ã«æ»ãã¾ãããã
ãã«ãã¼ã使ãããå¾ãã¹ãã¼ããã·ã³ã«ãã®ã¡ã½ãã (CopyStreamToStreamAsync
) ã®å¼æ° (source
, destination
) ãæ ¼ç´ããã¦ãã¾ãã
ãããã®å¼æ°ã¯ã¹ãã¼ããã·ã³ã® MoveNext
ãå¼ã³åºãããç¶æ
ãé²ãã§ããã¡ã½ããæ¬ä½ã®å®è£
ããå©ç¨ã§ããå¿
è¦ãããã¾ãã
ãã®ãããããã®å¼æ°ãã¹ãã¼ããã·ã³ã«æ ¼ç´ããªããã°ããã¾ããã
ã¾ããã¹ãã¼ããã·ã³ã¯åæç¶æ
(int <>1__state
) 㯠-1 ã§åæåããã¦ãã¾ãã
MoveNext
ãå¼ã³åºãããã¨ãã«ç¶æ
ã -1 ã§ããã°ãã¡ã½ãããåãã¦æåããå®è¡ããäºã¨å義ã«ãªãã¾ãã
ãã®æ¬¡ã«ãé常ã«å°å³ã ãã©å¤§äºãªè¡ãããã¾ãã
ãã«ãã¼ã® Start
ã¡ã½ããã®å¼ã³åºãã§ãã
Start
ã¡ã½ããã¯ãå®è³ªçã«ã¯ããã ãã§ãã
public struct AsyncTaskMethodBuilder { public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { // æ¬å½ã¯ããã¡ãã£ã¨ããããããããå®è³ªçã«ã¯ããã ãã // ãã®ããã¡ãã£ã¨ããããã®é¨åã¯å¾è¿° stateMachine.MoveNext(); } // ~~ çç¥ ~~ }
stateMachine.<>t__builder.Start(ref stateMachine)
ãå¼ã³åºããã¨ã¯ãå®è³ªçã«ã¯stateMachine.MoveNext()
ãå¼ã³åºãã ãã§ãã
ã«ãé¢ãããããªãã³ã³ãã¤ã©ã¯ãã®ããã«ç´æ¥å®è¡ããªãã®ã§ããããï¼ãªã Start
ãããã®ã§ããããï¼
ãã®çãã¯ãStart
ã«ã¯ç§ã説æãã以ä¸ã®ãã®ãããããã§ãã
ãããããããçè§£ããã«ã¯ã¾ã ExecutionContext
ãçè§£ããå¿
è¦ãããã¾ãã
ExecutionContext
æã ã¯ã¡ã½ããããã¡ã½ããã¸ç¶æ ãåãæ¸¡ãäºã«æ £ã親ããã§ãã¾ãã ã¡ã½ãããå¼ã¶éããã®ã¡ã½ããã«ãã©ã¡ã¼ã¿(仮弿°)ãå®ç¾©ããã¦ããããå¼ã³åºãå´ã¯ã¡ã½ããã«å¯¾ãã¦(å®)弿°ã渡ãã¦å¼ã³åºãã¾ãã ããã¯æç¤ºçã«ãã¼ã¿ãåãæ¸¡ãäºã§ãã
ãããããã£ã¨æé»çã«ãã¼ã¿ã渡ãäºãåºæ¥ã¾ãã ä¾ãã°ãã¡ã½ããA㯠static field ã«ãã¼ã¿ãæ ¼ç´ããæ¬¡ã«Bãå¼ã³åºããCãå¼ã³åºããDãå¼ã³åºããæçµçã«Eãå¼ã³åºã㦠A ãæ ¼ç´ãããã¼ã¿ãåå¾ãããã static field ã®å¤ãèªã¿åã...ã¨ãã£ããããªæãã§ãã¡ã½ããã®ãã©ã¡ã¼ã¿ãä»ããã«ãã©ããã§ static field ãæ ¼ç´ããã©ããã§ãã® static field ãèªã¿åºãã¦å©ç¨ããã¨ãã£ãå½¢ã®ãã¼ã¿ã®åãæ¸¡ãæ¹ãæé»çãªãã¼ã¿ã®æ¸¡ãæ¹ã§ãã ããã¯ãã ambient data ã¨å¼ã°ãããã®ã§ããã©ã¡ã¼ã¿ã§æ¸¡ãããã®ã§ã¯ãªãããã ã©ããããã«åå¨ããå¿ è¦ã§ããã°å©ç¨ãããã¨ãã§ãããã®ã§ãã ãã®ãããªå ´åãå¼ã³åºãå ã¨å¼ã³åºãå ã®éã«ã¯ãå¼ã³åºãå ãã©ããã«ãã¼ã¿ãæ ¼ç´ããå¼ã³åºãå ããã®ãã¼ã¿ãã©ããããããèªã¿è¾¼ãã¨ããæé»ã®äºè§£ãããã ãã§ãã
static field ã®å©ç¨ä»¥å¤ã«ããthread local ç¶æ
ã使ç¨ããäºãèããäºãã§ãã¾ãã
thread local ç¶æ
ã¯ã.NET ã§ã¯ [ThreadStatic]
屿§ãé©ç¨ããã static field ã ThreadLocal<T>
åã«ãã£ã¦å®ç¾ã§ãã¾ãã
thread local ãªãã¼ã¿ã¸ã®ã¢ã¯ã»ã¹ã¯ããããã®å®è¡ã¹ã¬ããã ãã«å¶éããã¦ãããåã¹ã¬ããäºã«ç¬ç«åã³åé¢ãã¦ãã¾ãã
ããã使ãã°ãthread static ãªãã£ã¼ã«ãçã«ãã¼ã¿ãå
¥ãã¦ã¡ã½ãããå¼ã³åºããã¡ã½ãããå®äºããã夿´ã thread static ãªãã£ã¼ã«ãã«æ»ããã¨ã§ãæé»çã«ããã¨ãããã¦ãããã¼ã¿ãã¹ã¬ããæ¯ã«å®å
¨ã«åé¢ããå½¢ã§å©ç¨ã§ããããã«ãªãã¾ãã
ããããéåæã®å ´åã¯ã©ãã§ãããï¼ async ã¡ã½ãããå¼ã³åºãããã® async ã¡ã½ããå ã®ãã¸ãã¯ã ambient data ã«ã¢ã¯ã»ã¹ãããå ´åã«ã¯ä¸çç¸ã§ã¯ããã¾ããã ã¾ãã¯é常㮠static field ã« ambient data æ ¼ç´ããã¦ãããå ´åã¯ã©ãã§ããã? ãããã async ã¡ã½ããã¯å¹³è¡ãªãã並åã§å®è¡ããã¾ãã ãªã®ã§ããã async control flow ã§æ¸ãæãã static ãã£ã¼ã«ãã¯ãå¥ã® flow ã§æ¸ãæãããã¦ãã¾ãããã§ãã çµæçã«ãç«¶åãé²ãããã«ã¯ã1ã¤ã®ã¡ã½ããããå®è¡ãããã¨ãã§ããªããã¨ãããéåæå¦ççã«ã¯åãå ¥ããããªãå¶ç´ãçºçãã¦ãã¾ãã®ã§ãambient data ã®æ¸¡ãæ¹ã¨ãã¦æç«ããªãããã§ãã thread local ç¶æ ã使ç¨ããå ´åã¯ã©ãã§ãããï¼ ãããã¼ã¿ã thread static field ã«æ ¼ç´ããã¦ããã°ãasync ã¡ã½ããã¯ããã«ã¢ã¯ã»ã¹ãããã¨ãã§ãã¾ããããããæå¾ ããæåãããã®ã¯å¼ã³åºãå ã®ã¹ã¬ããã§åæçã«å®è¡ãããªããªãæç¹ã¾ã§ã§ãã 使 ãªã await ãã task ãåæçã«å®äºãããµã¹ãã³ãããå ´åããã® task ãå®äºã次第 continuation ã®å®è¡ãã¹ã±ã¸ã¥ã¼ã«ããã¾ãããcontinuation ãã©ãã®ã¹ã¬ããã§å®è¡ããããã¯åããã¾ããã
ããã¯é常ã«å¤ãã®äººãåéããã¦ããäºã§ãã WPF ãªã©ã® GUI ãã¬ã¼ã ã¯ã¼ã¯ã§ã¯ await ã§å¾ æ©ãã¦ãããã®ãå®äºããcontinuation ãå®è¡ãããéã« UI ã¹ã¬ããã«æ»ã£ã¦ããæåãããã®ã§åéããã人ãå¤ãããã§ãããasync/await èªä½ã«ããã¨ãã¨å®è¡ããã¦ããã¹ã¬ããã«æ»ã£ã¦ãããã¨ããæ©è½ã¯ããã¾ãããUI ã¹ã¬ããã«æ»ã£ã¦ããã®ã¯ GUI ãã¬ã¼ã ã¯ã¼ã¯å´ã§
SynchronizationContext
ããã®ãããªä»äºãããããã«å®è£ ããã¦ãããããã¨ããã ãã®äºã§ãããæ»ã£ã¦ãããã¨ããããããcontinuation ã UI ã¹ã¬ããã§å®è¡ããããã¨è¨ã£ãæ¹ã誤解ãçã¾ããªããããããªãã§ãããçµæçã«æ»ã£ã¦ããããã«è¦ããã ããªã®ã§ãã
ãã®ãã async ã¡ã½ããã«ã¯ async ã¡ã½ããã«é©ãã ambient data ãããã¼ãããããã®ä»çµã¿ãå¿ è¦ã§ãã ã¤ã¾ããåæå®è¡å¯è½ã§ãã¹ã¬ããã夿´ãã¦ãåé¡ã®ãªããasync ã¡ã½ããç¨ã®è«çããã¼ãå®ç¾ãããããã®ä»çµã¿ã§ãã
ããã§ãExecutionContext
ã®ç»å ´ã§ãã
ExecutionContext
㯠ambient data ãéåææä½ããéåææä½ã¸ããã¼ãããããã®ã¢ãã§ãã
ããéåææä½ãéå§ãããã¨ãExecutionContext
ã¯ãã£ãã㣠& ä¿åããããã®éåææä½ã® continuation ãå®è¡ãããã¿ã¤ãã³ã°ã§ãã£ããã£ãã¦ããã ExecutionContext
ããªã¹ãã¢ããäºã§ã
ambient data ãããã¼ãããäºãã§ããããã«ãªãã¾ãã
ExecutionContext
㯠.NET Framework 㨠.NET Core ã§ã¯ããªãä¹é¢ãããã¾ãã
.NET Framework ã«ããã ExecutionContext
ã¯ãLogicalCallContext
ã SecurityContext
ãªã©æ§ã
ãªã³ã³ããã¹ããå
å
ãããããå
¨ã¦ã®ã³ã³ããã¹ããããã¼ããã¦ãã¾ããã
䏿¹ã§ã.NET Core ã«ããã¦ã¯ãAsyncLocal<T>
ã®ã¿ãæ ¼ç´ãããããããã¼ããã ãã®ãã®ã«ãªãã¾ããã
å°ã ExecutionContext
ãå
·ä½çãªæåãè¦ã¦ã¿ã¾ãããã
以ä¸ã®ãã㪠AsyncLocal<T>
ã使ãã³ã¼ãããã£ãæãConsole.WriteLine
ã¯ä½ã表示ããã§ããããï¼
var number = new AsyncLocal<int>(); number.Value = 42; ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(number.Value)); number.Value = 0;
çãã¯ãå¿
ã 42
ã§ããå¿
ãã§ãã
ãªããªã QueueUserWorkItem
ãå¼ã³åºãããéãå
é¨ã§ã¯ ExecutionContext
ããã£ããã£ãã¦ããããã®ãã£ããã£ããã ExecutionContext
ã«ã¯ AsyncLocal<int> number
ãå«ã¾ãã¦ãã¾ãã
ããã¦ãå®éã«ã¹ã¬ãããã¼ã«ã§å®è¡ããéã«ã¯ããã£ããã£ããã ExecutionContext
ã復å
ãããã®ã§ãå¿
ã 42
ã表示ãããããã«ãªãã¾ããnumber.Value = 0
ã¯ãã£ããã£ãããå¾ã«å®è¡ããã¦ãã®ã§ããã£ããã£ããæç¹ã® AsyncLocal<int> number
ã®å¤ã«ã¯å½±é¿ããªãããã§ãã
以ä¸ã®ããã«ç¬èªã®ã¹ã¬ãããã¼ã«ã£ã½ããã®ãå®è£ ããã¨ãããåããããããªãã§ãããã
using System.Collections.Concurrent; var number = new AsyncLocal<int>(); number.Value = 42; MyThreadPool.QueueUserWorkItem(() => Console.WriteLine(number.Value)); number.Value = 0; Console.ReadLine(); class MyThreadPool { private static readonly BlockingCollection<(Action, ExecutionContext?)> s_workItems = new(); public static void QueueUserWorkItem(Action workItem) { // ãã¥ã¼ã«å ¥ããã¾ãã«ãExecutionContext ããã£ããã£ããworkItem ã¨åããã¦ã¨ã³ãã¥ã¼ããã var context = ExecutionContext.Capture(); s_workItems.Add((workItem, context)); } static MyThreadPool() { for (int i = 0; i < Environment.ProcessorCount; i++) { var thread = new Thread(() => { while (true) { (Action action, ExecutionContext? ec) = s_workItems.Take(); if (ec is null) { action(); } else { // ãã£ããã£ãã ExecutionContext ä¸ã§å®è¡ããï¼ ExecutionContext.Run(ec, s => ((Action)s!)(), action); } } }); thread.IsBackground = true; thread.UnsafeStart(); } } }
ä½è«ã§ãããMyThreadPool
ã® static constructor ã§ UnsafeStart
ãå¼ã³åºãããã¨ã«ãæ°ã¥ãã§ãããã?
æ°ããã¹ã¬ããã®éå§ã¯ ExecutionContext
ãããã¼ãããã¹ãã¨ããã§ãã
å®éãThread
ã® Start
ã¡ã½ãã㯠ExecutionContext.Capture
ã使ã£ã¦ç¾å¨ã® ExecutionContext
ããã£ãã㣠& Thread
ã®ãã£ã¼ã«ãã«ä¿åããThread
ã®ã³ã³ã¹ãã©ã¯ã¿ã§æ¸¡ãã ThreadStart
ããªã²ã¼ããå¼ã³åºãã¨ãã«ãã®ãã£ããã£ããã³ã³ããã¹ãããªã¹ãã¢ãã¦ä½¿ã£ã¦ãã¾ãã
ããããMyThreadPool
ã®ä¾ã§ã¯ãstatic constructor ãå®è¡ãããæã«ãã¾ãã¾åå¨ãã ExecutionContext
ã Thread
ã«åãè¾¼ã¾ããããªãã®ã§ (ãã¢ãããè¤éã«ãªããã)ãStart
ã¡ã½ããã®ä»£ããã« UnsafeStart
ã¡ã½ããã使ç¨ãã¦ãã¾ãã
Unsafeã§å§ã¾ãã¹ã¬ããé¢é£ã¡ã½ããã¯ãExecutionContext
ããã£ããã£ããªããªãã¾ãããã®äºãé¤ãã°ãUnsafe æ¥é è¾ãæããªãã¡ã½ããã¯å
¨ãåãåä½ããã¾ãã
ãã¨ãã°ãThread.Start
ã ExecutionContext
ããã£ããã£ããã®ã«å¯¾ãã¦ãThread.UnsafeStart
㯠ExecutionContext
ããã£ããã£ãã¾ãããããã以å¤ã®åä½ã¯åãã§ãã
builder.Start() ã®éè¦æ§
ExecutionContext
ã®ã話ã¯ããã¾ã§ã«ãã¾ãããã
ãã¨ãã¨ãªãã®ã話ããã¦ãããã¨ããã¨ãasync ã¡ã½ããã¯ã³ã³ãã¤ã©ãã¹ãã¼ããã·ã³ä½ã£ãããªãã ããã¦ããã®ã¹ãã¼ããã·ã³ã¯ãã«ãã¼ã® Start
ã¡ã½ããã§ MoveNext
ããã¦ãããã¨ããã話ããã¦ãã¾ããã
ããã¦ãã® Start
ã¡ã½ããã¯ä½æ
éè¦ãï¼ã¨ããã話ã§ããã
ãªãéè¦ããçè§£ããããã«ã¯ãExecutionContext
ãé¿ãã¦éããªãã®ã§è§£èª¬ããã¦ããããã§ãã
å
ã»ã© Start
ã¡ã½ããã¯æ¬è³ªçã«ã¯ stateMachine.MoveNext()
ãããã ããã¨è¨ãã¾ããã
public struct AsyncTaskMethodBuilder { public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { // æ¬å½ã¯ããã¡ãã£ã¨ããããããããå®è³ªçã«ã¯ããã ãã // ãã®ããã¡ãã£ã¨ããããã®é¨åã¯ãå®ã¯ ExecutionContext ã«é¢ãããã¨ã ã£ãã®ã§ãï¼ stateMachine.MoveNext(); } // ~~ çç¥ ~~ }
ä¸è¨ã®ãã®ã¯é常ã«ç°¡ç´ åãããã®ã§ãããå®éã«ã¯ä»¥ä¸ã®ãããªå½¢ã«ãªã£ã¦ãã¾ãã
public struct AsyncTaskMethodBuilder { public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { // ExecutionContext.Capture() ã¨å®è³ªçã«åãã ExecutionContext previous = Thread.CurrentThread._executionContext; try { stateMachine.MoveNext(); } finally { ExecutionContext.Restore(previous); } } // ~~ çç¥ ~~ }
ããã§ããå®éã«ã¯ stateMachine.MoveNext()
ãå¼ã³åºãã ãã§ãªããç¾å¨ã® ExecutionContext
ãåå¾ãã¦ãã MoveNext
ãå®è¡ããçµäºæã«ç¾å¨ã® ExecutionContext
ã MoveNext
å®è¡åã®ç¶æ
ã«æ»ãã¨ããå¦çãè¡ã£ã¦ãã¾ãã
ãã®çç±ã¯ãasync ã¡ã½ããã®å¼ã³åºãå 㸠ambient data ã®ãªã¼ã¯ãé²ãããã§ãã å ·ä½ä¾ãè¦ã¦ããã¾ãããã
async Task ElevateAsAdminAndRunAsync() { using (WindowsIdentity identity = LoginAdmin()) { // Impersonation : ãå½è£ ã ã®æ // WindowsImpersonationContext 㯠.NET Framework ã«ãããªã class. // https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.windowsimpersonationcontext?view=netframework-4.8.1 using (WindowsImpersonationContext impersonatedUser = identity.Impersonate()) { await DoSensitiveWorkAsync(); } } }
Impersonation (å½è£
) ã¨ã¯ãç¾å¨ã®ã¦ã¼ã¶ã¼ã«é¢ããæ
å ±ããä»ã®èª°ãã®æ
å ±ã«å¤æ´ãããã¨ã§ãã
ããã«ããã³ã¼ãã¯ç¾å¨ã®ã¦ã¼ã¶ã§ã¯ãªããä»ã®èª°ãã¨ãã¦è¡åãããã®ã¢ã¯ã»ã¹æ¨©ã使ç¨ãããã¨ãã§ãã¾ãã
(åè: å½è£
ã¨ã¯ä½ãï¼)
.NET ã§ãã®ãããªå½è£
ã¯éåææä½ã¨ãã¦ããã¼ãã¾ããããã㯠ExecutionContext
ã« ambient data ãæ ¼ç´ãããäºãæå³ãã¾ãã
ããã§ãStart
ã¡ã½ããã finaly ã§å®è¡åã® ExecutionContext
ã復å
ããªãã£ãå ´åã®äºãæ³åãã¦ã¿ã¦ãã ããã
Task t = ElevateAsAdminAndRunAsync(); PrintUser(); await t;
ä¸è¨ã®ã³ã¼ãã§ã¯ãElevateAsAdminAndRunAsync
å
é¨ã§ ExecutionContext
ã«å¤æ´ããããã
DoSensitiveWorkAsync
ããã®å
é¨ã®å®äºãã¦ããªã Task
ãªãã¸ã§ã¯ãçãåã㦠await ããã¿ã¤ãã³ã°ã§ãµã¹ãã³ããçºçããã¡ã½ããã®å¼ã³åºãå
ã«åæçã«è¿ã£ã¦ãã¾ãã
ãã®æããã ExecutionContext
ã ElevateAsAdminAndRunAsync
ã¡ã½ããå¼ã³åºãåã®ãã®ã«å¾©å
ãã¦ããªãã£ãããã©ããªãã§ããããï¼
ãããPrintUser
ã¡ã½ããã§ãElevateAsAdminAndRunAsync
å
é¨ã§å¤æ´ããã ExecutionContext
ãèªãã¦ãã¾ãã¾ããããå½ç¶æå³ããªã形㧠ambient data ãèªã¿è¾¼ãã¦ãã¾ãã¾ãã
ããã¯åé¡ã§ãã
ãã®ãããStart
ã¡ã½ããã§ã¯ãExecutionContext
ã¸ã®å¤æ´ãåæã¡ã½ããã®å¼ã³åºãå
ã«æ¼ããªãããã«ãããããfinaly ã§å®è¡åã® ExecutionContext
ã復å
ãããããªé²è¡ãè¡ã£ã¦ãã¾ãã
IAsyncStateMachine.MoveNext
async ã¡ã½ãããå®è¡ãããããã¹ãã¼ããã·ã³ã®æ§é ä½ãåæåããããã«ãã¼ã® Start
ã¡ã½ãããå¼ã°ããã¹ãã¼ããã·ã³ã® MoveNext
ã¡ã½ãããå¼ã³åºããã¾ãã
MoveNext
ã¡ã½ããã¯éçºè
ã®ã¡ã½ããã«ãã£ããã¸ãã¯ããã¹ã¦å«ã¿ã¤ã¤ããå¤ãã®å¤æ´(ã©ãã¾ã§ç¶æ
ãé²ãã ããä¿åãã¦ãããã£ã¼ã«ãã®æ´æ°ããããã®ç¶æ
ã«å¿ãã¦ã¸ã£ã³ããã switch æç)ãå ãããã¦ããã¡ã½ããã§ãã
ã¾ãããã®ã¡ã½ããã®åºæ¬çãªã¨ããããè¦ã¦ã¿ã¾ãããã
ã³ã³ãã¤ã©ãCopyStreamToStreamAsync
ã¡ã½ãããã³ã³ãã¤ã«ãããã®ãéã³ã³ãã¤ã«ããã©ã®ãããªã³ã¼ããçæããã¦ãããè¦ã¦çãã¾ãããã
以ä¸ã¯ãéã³ã³ãã¤ã«ããã³ã¼ãã®ãã¡ try ãããã¯ããã¹ã¦çç¥ãããã®ã§ãã
private void MoveNext() { try { // ~~ çç¥ ~~ } catch (Exception exception) { <>1__state = -2; <buffer>5__2 = null; <>t__builder.SetException(exception); return; } <>1__state = -2; <buffer>5__2 = null; <>t__builder.SetResult(); }
MoveNext
ã¡ã½ããã«ã¯ãå
¨ã¦ã®ä½æ¥ãçµäºããæç¹ã§ async ã¡ã½ããããè¿ããã Task
ãªãã¸ã§ã¯ããå¿
ãå®äº(Task.IsCompleted
ã true
ã®ç¶æ
)ãããã¨ãã責åãè² ã£ã¦ãã¾ãã
ãã try ãããã¯æ¬ä½ã§å¦çãããªãä¾å¤ãæããããããTask
ãªãã¸ã§ã¯ãã¯ãã®ä¾å¤ã«ãã£ã¦å¤±æãããããäºã«ãªãã¾ãã
ã¾ããasync ã¡ã½ãããæ£å¸¸ã«çµäºã«è³ã£ãå ´åãè¿ããã Task
ãªãã¸ã§ã¯ãã¯æ£å¸¸ã«å®äºãã¾ãã
ãããã®å ´åããã¹ãã¼ããã·ã³ã®ç¶æ
ã async ã¡ã½ããã®æå/失æã«é¢ããããå®äºç¶æ
ã«ãªãããè¨å®ãã¦ããã®ã§ãã
ã¾ããã® Task
ãªãã¸ã§ã¯ãã«å¯¾ããå®äºã¯ããã«ãã¼ã® SetException
㨠SetResult
ã¡ã½ãããçµç±ãã¦ããäºã«æ³¨æãã¦ãã ããã
async ã¡ã½ããã以åã«ãµã¹ãã³ããã¦ããå ´åããã«ãã¼ã¯ãã®ãµã¹ãã³ãã®å¦çã®ä¸é¨ã¨ãã¦æ¢ã« Task
ãªãã¸ã§ã¯ãã使ãã¦ããã¯ãã§ãã
ãã®å ´åãæ¢ã«ãã Task
ãªãã¸ã§ã¯ãã«å¯¾ã㦠SetException
/SetResult
ãå¼ã³åºããæã«ãã® Task
ãå®äºãã¾ãã
ããããasync ã¡ã½ããã以åã«ãµã¹ãã³ããã¦ããªãå ´åã¯ãã¾ã Task
ãªãã¸ã§ã¯ãã使ãã¦ããããå¼ã³åºãå
ã«ãä½ãè¿ãã¦ããªãããããã«ãã¼ã¯ Task
ãªãã¸ã§ã¯ãã使ããæ¹æ³ã«ã¤ãã¦ããæè»æ§ãæã£ã¦ãã¾ããä¾ãã°ãã®æè»æ§ã®ãé°ã§ãæ¯åº¦ Task
ãªãã¸ã§ã¯ãã allocate ããããããTask.CompletedTask
çãã£ãã·ã¥ããããªãã¸ã§ã¯ããè¿ãã¦ç¡é§ãª allocation ãé¿ãããããäºãã§ãã¾ãã
ã¾ãããã«ãã¼ã¯ä½æãã Task
ãªãã¸ã§ã¯ããé©åãªç¶æ
ã«ãã責åãæ
ã£ã¦ãã¾ãã
Task
ãªãã¸ã§ã¯ãã«ã¯æçµç¶æ
ã¨ãã¦ãRanToCompletion
(success) / Faulted
/ Canceled
ã®3ã¤ã®ç¶æ
ãããã¾ãã
AsyncTaskMethodBuilder
ã® SetException
ã¡ã½ããã¯ã OperationCanceledException
ãç¹å¥æ±ãããæ¸¡ãããä¾å¤ãOperationCanceledException
ãããã¯OperationCanceledException
ããæ´¾çããã¯ã©ã¹ã ã£ãå ´å㯠Task
ãªãã¸ã§ã¯ãã®ç¶æ
ã TaskStatus.Canceled
ã«é·ç§»ããããã以å¤ã¯ TaskStatus.Faulted
ã¨ãã¦ã¿ã¹ã¯ãçµäºãã¾ãã
ããã¾ã§ã§ãã©ã¤ããµã¤ã¯ã«ã®å´é¢ãçè§£ããã®ã§ã次㫠try ãããã¯ãåãã MoveNext
ã¡ã½ãããè¦ã¦è¡ãã¾ãããã
private void MoveNext() { try { int num = <>1__state; TaskAwaiter<int> awaiter; if (num != 0) { if (num != 1) { <buffer>5__2 = new byte[4096]; goto IL_008b; } awaiter = <>u__2; <>u__2 = default(TaskAwaiter<int>); num = (<>1__state = -1); goto IL_00f0; } TaskAwaiter awaiter2 = <>u__1; <>u__1 = default(TaskAwaiter); num = (<>1__state = -1); IL_0084: awaiter2.GetResult(); IL_008b: awaiter = source.ReadAsync(<buffer>5__2, 0, <buffer>5__2.Length).GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 1); <>u__2 = awaiter; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); return; } IL_00f0: int result; if ((result = awaiter.GetResult()) != 0) { awaiter2 = destination.WriteAsync(<buffer>5__2, 0, result).GetAwaiter(); if (!awaiter2.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter2; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref this); return; } goto IL_0084; } } catch (Exception exception) { <>1__state = -2; <buffer>5__2 = null; <>t__builder.SetException(exception); return; } <>1__state = -2; <buffer>5__2 = null; <>t__builder.SetResult(); }
ã¹ãã¼ããã·ã³ã®ã¹ãã¼ã(<>1__state
)㯠-1 ã§åæåããã¦ãããã¨ãæãåºãã¦ãã ããã
MoveNext
ã®ä¸ã§ãããã¾ã â ãã®ã¹ãã¼ã(ç¾å¨ã¯ num
ãã¼ã«ã«ã«æ ¼ç´ããã¦ãã) ã 0 ã§ã 1 ã§ããªããã¨ã確èªããâ¡ä¸æãããã¡ã使ããã³ã¼ããå®è¡ããâ¢ã©ãã« IL_008b
ã«åå²ããâ£stream.ReadAsync
ãå¼ã³åºããã¨ã«ãªãã¾ãã
ã¤ã¾ãéçºè
ã®ã³ã¼ãã CopyStreamToStreamAsync
ãå¼ã³åºãããã®ã¡ã½ããã®æçµçãªå®äºã表ã task ãã¾ã è¿ãã¦ããªãç¶æ
ã§ã¯ãã¾ã åæçã«å®è¡ããã¦ããã®ã§ãã
Stream.ReadAsync
ãå¼ã³åºãã¨ãTask<int>
åã®ãªãã¸ã§ã¯ããè¿ããã¾ãã
ReadAsync
ã¯åæçã«å®äºããã®ããããã¾ããããéåæã§å®è¡ããããã®ã®ããã¾ãã®éãã§æ¢ã«å®äºãã¦ããããããã¾ããããã¾ã å®äºãã¦ããªãã®ããããã¾ããã
ãããã«ãããTask<int>
ãªãã¸ã§ã¯ãã¯å®äºãã¦ãããããªãã£ããããããã§ãããã³ã³ãã¤ã©ã¯ãã®Task<int>
ãªãã¸ã§ã¯ãã®ç¶æ
ãæ¤æ»ãã¦ãã©ã®ããã«å¦çãé²ããããæ±ºå®ããã³ã¼ããçæãã¾ãã
Task<int>
ãªãã¸ã§ã¯ãããã§ã«å®äºãã¦ããã°(åæçã«å®äºãããããã§ãã¯ããæç¹ã§å®äºãã¦ãããã¯é¢ä¿ãªã)ããã®ã¡ã½ããã®ã³ã¼ãã¯åæçã«å®è¡ãç¶ãããã¨ãã§ãã¾ããããã Task<int>
ãªãã¸ã§ã¯ããå®äºããªãå ´åã®ããã«ãã³ã³ãã¤ã©ã¯ Task ã« continuation ãããã¯ããã³ã¼ãã使ããå¿
è¦ãããã¾ãã
ãã®ãããtask ã«ãçµãã£ããï¼ãã¨å°ããã³ã¼ããçæããå¿
è¦ãããã¾ãã
ãããããããç´æ¥ Task
ãªãã¸ã§ã¯ãã«å°ããã¹ããªã®ã§ããããï¼
C# ã§ await ãããã¨ãã§ããã®ã System.Threading.Tasks.Task
ã ãã ã¨ããããããã¯å¶éçã§ãã
åæ§ã«ãC# ã³ã³ãã¤ã©ã await å¯è½ãªãã¹ã¦ã®åã«ã¤ãã¦ç¥ããªããã°ãªããªãã¨ãããããããã¾ãå¶éçã§ãã
ç´æ¥ Task
ãªãã¸ã§ã¯ãã«ãçµãã£ããï¼ãã¨å°ãããã®ä»£ãããããã㯠await å¯è½ãªå
¨ã¦ã®åã«ã³ã³ãã¤ã©ã対å¿ãããããã代ããã«ãC# 㯠ãawaiter ãã¿ã¼ã³ãã¨ãã API ã®ãã¿ã¼ã³ãæ¡ç¨ãã¦ãã¾ãã
é©åãªãenumerable ãã¿ã¼ã³ããæä¾ãããã®ã§ããã°ãä½ã§ã foreach ã§ããã®ã¨åãããã«ã
C# ã³ã¼ãä¸ã§é©åãªãawaiter ãã¿ã¼ã³ããå®è£
ãã¦ãããã®ã§ããã°ãä½ã§ã await ã§ããããã«ãªã£ã¦ãã¾ãã
ä¾ãã°ãå
ã»ã©æ¸ãã MyTask
åãæ¡å¼µãã¦ãawaiter ãã¿ã¼ã³ããå®è£
ãããã¨ãã§ãã¾ãã
class MyTask { // ~~ çç¥ ~~ public MyTaskAwaiter GetAwaiter() => new MyTaskAwaiter { _task = this }; public struct MyTaskAwaiter : ICriticalNotifyCompletion { internal MyTask _task; public bool IsCompleted => _task._completed; public void OnCompleted(Action continuation) => _task.ContinueWith(_ => continuation()); public void UnsafeOnCompleted(Action continuation) => _task.ContinueWith(_ => continuation()); public void GetResult() => _task.Wait(); } }
GetAwaiter()
ã¡ã½ãããå
¬éãã¦ãããé©å㪠awaiter ãè¿ãã¦ããä»»æã®å㯠await ãããã¨ãã§ãã¾ãã
é©å㪠awaiter ã«ã¯ãIsCompleted
ããããã£ã¨ GetResult
/OnCompleted
(/UnsafeOnCompleted
) ã¡ã½ãããå®ç¾©ããã¦ããå¿
è¦ãããã¾ãã
ãã㦠IsCompleted
ããããã£ã¯ãIsCompleted
ãå¼ã³åºãããæç¹ã§ãæä½ããã§ã«å®äºãããã©ããããã§ãã¯ããããã«ä½¿ç¨ããã¾ã(ã©ã®ããã«å®è¡ããããã¯é¢ä¿ããã¾ãã)ã
ã³ã³ãã¤ã©ã«ãã£ã¦å±éããã MoveNext
ã¡ã½ããå
ã§ã¯ IL_008b
ã«ã¸ã£ã³ãããå¾ãReadAsync
ããè¿ããã Task
ãªãã¸ã§ã¯ãã«å¯¾ã㦠GetAwaiter
ãå¼ã³åºããããã® awaiter ã® IsCompleted
ãå¼ã³åºããã¦ãã¾ãã
IsCompleted
ã true
ãè¿ããå ´åãIL_00f0
ã«é²ã¿ãawaiter ã®å¥ã®ã¡ã³ãã¼ GetResult()
ãå¼ã³åºããã¨ã«ãªãã¾ãã
æä½ã失æããå ´åãGetResult()
ã¯ä¾å¤ãæã㦠async ã¡ã½ããã®å¤ã«ä¾å¤ã伿ãããå½¹å²ãæããã¾ãã
ãã以å¤ã®å ´åãã¤ã¾ãå¦çã®çµæãããã°ãGetResult()
ã¯ãããè¿ãå½¹å²ãæããã¾ãã
ReadAsync
ã®å ´åãå¦çã®çµæ(ã¤ã¾ã GetResult()
ãè¿ããçµæ)ã 0 ã§ããã°ãèªã¿æ¸ãã«ã¼ãããæãåºããSetResult()
ãå¼ã³åºãã¡ã½ããã®æ«å°¾ã«ç§»åãã¦å®äºã§ãã
ããã§æ¬å½ã«è峿·±ãã®ã¯ãIsCompleted
ãã§ãã¯ã false
ãè¿ããå ´åã«å®éã©ããªããã§ãã
IsCompleted
ã false
ãè¿ããå ´åãawait ãããå¦çãå®äºããã¾ã§ async ã¡ã½ããã®å®è¡ããµã¹ãã³ãããå¿
è¦ãããã¾ãã
ãã㯠MoveNext
ãã Start
ã«è¿ãã¨ããäºã§ãããããã¦ã¡ã½ããã®å¼ã³åºãå
ã« Task
ãªãã¸ã§ã¯ããè¿ããã¨ãæå³ãã¾ãã
ããã Task
ãªãã¸ã§ã¯ããè¿ãåã«ãå¾
æ©ä¸ã® Task
ãªãã¸ã§ã¯ãã« continuation ãããã¯ããå¿
è¦ãããã¾ã(ãªããã¹ã¿ãã¯ãç©ã¾ãç¶ããäºãé¿ããããã«ãIsCompleted
ã false ãè¿ããå¾ãã¤ãcontinuation ãããã¯ããåã«éåæå¦çãå®äºããå ´åãcontinuation ã¯ã¹ã¬ãããã¼ã«ããéåæã«å¼ã³åºãå¿
è¦ãããããã¥ã¼ã«æããããäºã«æ³¨æãã¾ããã)ã
ã¾ãã(é©åã«å®è£
ããããã°) ä½ã§ã await ãããã¨ãã§ããããã«ãããããTask
ãªãã¸ã§ã¯ãã«ç´æ¥ã¢ã¯ã»ã¹ãããããªäºã¯ãã¾ããã
ãã®ä»£ããã«ä½ããã®æ¹æ³ãç¨ã㦠continuation ã®ããã¯ãè¡ãå¿
è¦ãããã¾ãã
ã¤ã¾ã Task
ã¯ããèªä½ã ContinueWith
ã¡ã½ãããªã©ãæã£ã¦ãããcontinuation ããµãã¼ããã¦ãã¾ãããcontinuation ãè¨å®ã§ããã¡ã½ãããå
¬éããã®ã¯ãGetAwaiter
ã¡ã½ããããè¿ããã TaskAwaiter
ã§ããã¹ãã¨ããäºã§ããããï¼ continuation ãããã¯ããããã®ã¡ã½ããã awaiter ã«ããã¨ããäºã§ããããï¼
ã¯ãããã®éãã§ãã
ãawaiter ãã¿ã¼ã³ãã§ã¯ãawaiter ã INotifyCompletion
interface ãå®è£
ããå¿
è¦ãããããã®ã¤ã³ã¿ã¼ãã§ã¤ã¹ã«ã¯1ã¤ã®ã¡ã½ãã void OnCompleted(Action continuation)
ãå®ç¾©ããã¦ãã¾ãã
ã¾ããawaiter ãã¿ã¼ã³ã«ã¯ ICriticalNotifyCompletion
interface ãå®è£
ããã¨ãã鏿è¢ãããã¾ãã
ICriticalNotifyCompletion
㯠INotifyCompletion
ãç¶æ¿ããvoid UnsafeOnCompleted(Action continuation)
ã¨ããã¡ã½ããã追å ã§å®ç¾©ããã¦ãã¾ãã
åã« ãUnsafeã ã¨ãããã¬ãã£ãã¯ã¹ãä»ããã®ã® ExecutionContext
ã®æ±ãã«ã¤ãã¦èª¬æãã¾ãããããã®2ã¤ã®ã¡ã½ããã®éãã¯ä½ãã¨ããã¨ãã©ã¡ãã continuation ãããã¯ãããã®ã§ãããOnCompleted
ã ExecutionContext
ãããã¼ãããå¿
è¦ãããã®ã«å¯¾ããUnsafeOnCompleted
㯠ExecutionContext
ãããã¼ãããå¿
è¦ããªãã®ã§ãã
INotifyCompletion.OnCompleted
㨠ICriticalNotifyCompletion.UnsafeOnCompleted
ã¨ãã2ã¤ã®ç°ãªãã¡ã½ãããå¿
è¦ãªã®ã¯ã主ã«ã³ã¼ãã¢ã¯ã»ã¹ã»ãã¥ãªã㣠(CAS) ã«é¢é£ããæ´å²çãªãã®ã§ãã
CASã¯ãã¯ã .NET Core ã«ã¯åå¨ããã.NET Framework ã§ã¯ããã©ã«ãã§ãªãã«ãªã£ã¦ãããã¬ã¬ã·ã¼ãª partial trust æ©è½ã鏿ããå ´åã«ã®ã¿ç¾ããã¾ãã
partial trust ãç¨ããå ´åãCAS æ
å ±ã¯ ExecutionContext
ã®ä¸é¨ã¨ãã¦ããã¼ããããããããããã¼ãããªããã¨ã¯ãå®å
¨ã§ã¯ãªãããã¨ã«ãªãããã®ãã ExecutionContext
ãããã¼ããªãã¡ã½ããã«ã¯ãUnsafeãã¨ãããã¬ãã£ãã¯ã¹ãä»ãããã¦ãã¾ããã
ã¾ãããã®ãããªã¡ã½ããã¯[SecurityCritical]
ã¨ãããé¨åçã«ä¿¡é ¼ãããã³ã¼ã㯠[SecurityCritical]
ã¡ã½ãããå¼ã³åºããã¨ãã§ãã¾ããã
ãã®çµæãOnCompleted
ã®2ã¤ã®ããªã¢ã³ãã使ãããã³ã³ãã¤ã©ã¯ UnsafeOnCompleted
ãæä¾ãããå ´åã¯ããã使ç¨ãããã¨ã好ã¿ã¾ãããOnCompleted
ããªã¢ã³ã㯠awaiter ã partial trust ããµãã¼ãããå¿
è¦ãããå ´åã«åãã¦å¸¸ã«æä¾ããã¦ãã¾ãã
ããããasync ã¡ã½ããã®è¦³ç¹ããã¯ããã«ãã¼ã¯å¸¸ã« await ãã¤ã³ãã§ ExecutionContext
ãããã¼ããã®ã§ãawaiter ã§ã ExecutionContext
ãããã¼ããã®ã¯ä¸è¦ã§éè¤ããå¦çã¨ãªãã¾ãã
awaiter 㯠continuation ãããã¯ããã¡ã½ãããå ¬éãã¦ãã¾ãã ã³ã³ãã¤ã©ã¯ãã®ã¡ã½ãããç´æ¥ä½¿ããã¨ãã§ãã¾ãããé常ã«éè¦ãªåé¡ãããã¾ãã ãã㯠continuation ãã©ãããã¹ããããªãã¸ã§ã¯ããã©ãé¢é£ä»ããããã¨ããåé¡ã§ãã
ã¹ãã¼ããã·ã³æ§é ä½ã¯ã¹ã¿ãã¯ä¸ã«ãããç¾å¨å®è¡ä¸ã® MoveNext
ã¯ãã®ã¹ãã¼ããã·ã³ã«å¯¾ããã¡ã½ããå¼ã³åºãã§ãããã¨ãæãåºãã¦ãã ããã
ã¤ã¾ãããµã¹ãã³ãæã«ã¯ãã®ã¹ãã¼ããã·ã³ããã¼ãä¸ã®ã©ããã«ã³ãã¼ããå¿
è¦ãããã¾ãã
ãªããªãã¹ã¿ãã¯ã¯ãã®ã¹ã¬ããä¸ã§å®è¡ããããç¾å¨ã®ããã¼ã¨ã¯å
¨ãç¡é¢ä¿ãªå¾ç¶ã®ä½æ¥ã«ä½¿ç¨ããããã¨ã«ãªãããã§ãã
ããã¦ãcontinuation ã§ã¯ããã¼ãä¸ã«ã³ãã¼ãããã¹ãã¼ããã·ã³ã® MoveNext
ã¡ã½ãããå¼ã³åºãå¿
è¦ãããã¾ãã
ããã«ãããã§ã ExecutionContext
ãé¢ä¿ãã¦ãã¾ãã
ã¹ãã¼ããã·ã³ã¯ãExecutionContext
ã«æ ¼ç´ããã ambient data ããµã¹ãã³ããããæç¹ã§ç¢ºå®ã«ãã£ããã£ããåéæç¹ã§å¾©å
ããå¿
è¦ããããã¤ã¾ã continuation ã«ãã£ããã£ãã ExecutionContext
ãåãè¾¼ãå¿
è¦ãããã¾ãã
è¦ããã«ãã¹ãã¼ããã·ã³ã® MoveNext
ãæãããªã²ã¼ããä½ãã ãã§ã¯ä¸ååãªã®ã§ãã
ã¾ããµã¹ãã³ãæã«ã¹ãã¼ããã·ã³ã® MoveNext
ãæãããªã²ã¼ããé½åº¦ä½æããã¨ããã®ãã³ã«ã¹ãã¼ããã·ã³æ§é ä½ãããã¯ã¹åãã¦(ä»ã®ãªãã¸ã§ã¯ãã®ä¸é¨ã¨ãã¦ãã§ã«ãã¼ãä¸ã«ããå ´åã§ã)ã追å ã®ããªã²ã¼ãã«å¯¾ãã allocation ãçºçãã¾ã(ããªã²ã¼ãã®ãã®ãªãã¸ã§ã¯ãåç
§ã¯ãæ°ãã«ããã¯ã¹åããæ§é ä½ã®ã³ãã¼ã¨ãªã)ã
ãããã¯æã¾ãããªããªã¼ãã¼ãããã§ãã
ãã®ãããasync ã¡ã½ãããæåã«å®è¡ããµã¹ãã³ããããæã®ã¿æ§é ä½ãã¹ã¿ãã¯ãããã¼ãã« boxing ãããã以å¤ã®ã¨ãã¯æ¢ã«ãã¼ãã«ãããªãã¸ã§ã¯ãã MoveNext
ã®ã¿ã¼ã²ããã¨ãã¦ä½¿ç¨ãããã®éç¨ã§æ£ããã³ã³ããã¹ãããã£ããã£ããåéæã«ãã®ãã£ããã£ããã³ã³ããã¹ãã使ç¨ãã¦å¦çãå®è¡ããã¨ãããè¤éãªæåãããå¿
è¦ãããã¾ãã
ããã¯ãã³ã³ãã¤ã©ãåºåãããããå¤ãã®ãã¸ãã¯ãå¿ è¦ã¨ãã¾ãã ããã¤ãã®çç±ããããããã¯ä»£ããã®ãã«ãã¼ã«ã«ãã»ã«åãããã¨èãã¦ãã¾ãã ã¾ã第ä¸ã«ãåã¦ã¼ã¶ã¼ã®ã¢ã»ã³ããªã«è¤éãªã³ã¼ããããããå«ã¾ãããã¨ã«ãªã£ã¦ãã¾ãããã 第äºã«ããã«ãã¼ãã¿ã¼ã³ã®å®è£ ã®ä¸ç°ã¨ãã¦ããã®ãã¸ãã¯ãã«ã¹ã¿ãã¤ãºã§ããããã«ããããã(å¾ã§ãã¼ãªã³ã°ã«ã¤ãã¦è©±ãã¨ãã«ããã®çç±ã®ä¾ãè¦ããã¨ã«ãªãã¾ã)ã ããã¦ç¬¬ä¸ã«ããã®ãã¸ãã¯ãé²åãããæ¹åãããã¨ã§ã以åã«ã³ã³ãã¤ã«ããããã¤ããªãããè¯ãå®è¡ãããããã«ãããããã§ãã ããã¯ä»®èª¬ã§ã¯ããã¾ããã å®éã« .NET Core 2.1 ã§ã¯ãã®ãµãã¼ãã®ããã®ã©ã¤ãã©ãªã³ã¼ããå®å ¨ã«è¦ç´ããã.NET Framework ã®ã¨ããããã¯ããã«å¹ççãªå®è¡ãã§ããããã«ãªãã¾ããã ã¾ãã.NET Framework ã§ã©ã®ããã«åä½ãã¦ããããæ£ç¢ºã«èª¿ã¹ã次ã«.NET Coreã§ã©ããªã£ã¦ããããè¦ã¦ããã¾ãã
C# ã³ã³ãã¤ã©ãçæããã³ã¼ããã¿ãã¨ããµã¹ãã³ããå¿ è¦ãªæã«ä»¥ä¸ãèµ·ããäºãåããã¾ãã
if (!awaiter.IsCompleted) { <>1__state = 1; <>u__2 = awaiter; // ãã«ãã¼ã«ãawaiter ã continuation ã¨ãã¦ãã®ã¹ãã¼ããã·ã³ãå¼ã¶ããããã«æ¥ç¶ããããä¾é ¼ãã <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); return; }
ã¹ãã¼ããã£ã¼ã«ã(<>1__state
)ã«ã¯ãã¡ã½ããã®åéæã«ã¸ã£ã³ããã¹ãå ´æã示ãã¹ãã¼ãIDãæ ¼ç´ãã¦ãã¾ãã
ããã¦ãåéå¾ã« GetResult
ãå¼ã³åºãããã«ä½¿ç¨ã§ããããã«ãawaiter èªä½ããã£ã¼ã«ãã«æ°¸ç¶åãã¦ãã¾ãã
ããã¦ãMoveNext
ã®å¼ã³åºãå
ã«æ»ãç´åã«ã<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this)
ãå¼ã³åºããawaiter ã«ãã®ã¹ãã¼ããã·ã³ã® continuation ãããã¯ãããããã«ãã¼ã«è¦æ±ãã¾ãã
(ãã«ãã¼ã® AwaitOnCompleted
ã§ã¯ãªããã«ãã¼ã® AwaitUnsafeOnCompleted
ãå¼ãã§ããã®ã¯ãawaiter ãICriticalNotifyCompletion
ãå®è£
ãã¦ããããã§ãã
ã¹ãã¼ããã·ã³ã¯ããã¼ãã¦ãã ExecutionContext
ãå¦çããã®ã§ awaiter ã«ããããæ±ããå¿
è¦ã¯ããã¾ããã
å
ã«è¿°ã¹ãããã«ãããã¯éè¤ãã¦ä¸è¦ãªãªã¼ãã¼ãããã«ãªãã ãã§ã)
ãã® AwaitUnsafeOnCompleted
ã¡ã½ãã (ãã«ãã¼ã®ã¡ã½ãã)ã®å®è£
ã¯ãããã«ã³ãã¼ãã¦ç¤ºãã«ã¯è¤éãããã®ã§ã.NET Framework ä¸ã§ä½ããã¦ããã®ãã以ä¸ã«è¦ç´ãã¾ãã
ExecutionContext.Capture()
ã使ã£ã¦ãç¾å¨ã®ã³ã³ããã¹ããåå¾ã- ãã£ããã£ããã
ExecutionContext
ã¨ããã¯ã¹åãããã¹ãã¼ããã·ã³ã®ä¸¡æ¹ãã©ããããããã®MoveNextRunner (class)
ãªãã¸ã§ã¯ãã allocate ãã¾ã(ãã®ã¡ã½ãããåãã¦ãµã¹ãã³ãããå ´åã¯ã¾ã æã£ã¦ããªãã®ã§ãnull ã placeholder ã¨ãã¦ä½¿ç¨ãã¾ã)ã - ããã¦ããã®
MoveNextRunner
ã®Run
ã¡ã½ããã«å¯¾ããAction
ããªã²ã¼ãã使ãã¾ãããããã¦ããã£ããã£ããExecutionContext
ã®ã³ã³ããã¹ãã§ãã¹ãã¼ããã·ã³ã®MoveNext
ãå¼ã³åºãããªã²ã¼ããåå¾ãããã¨ãã§ããã®ã§ãã - ãã®ã¡ã½ãããåãã¦ãµã¹ãã³ãããå ´åãã¾ã ããã¯ã¹åãããã¹ãã¼ããã·ã³ãæã£ã¦ããªãã®ã§ããã®æç¹ã§ããã¯ã¹åãã¾ããããã¯
IAsyncStateMachine
interface ã¨ãã¦åä»ãããããã¼ã«ã«å¤æ°ã«ãªãã¸ã§ã¯ããæ ¼ç´ãããã¨ã«ãã£ã¦ãã¼ãä¸ã«ãã£ã¦ããã¾ããããã¦ããã®ããã¯ã¹åãããã¹ãã¼ããã·ã³ã¯ãMoveNextRunner
ã«ä¿åããã¾ãã - ãããããé£ããã¹ãããã§ãã ã¾ããã¹ãã¼ããã·ã³æ§é ä½ã®å®ç¾©ãè¦ãã¨ã
public AsyncTaskMethodBuilder <>t__builder
ã¨ãããã«ãã¼ã®ãã£ã¼ã«ããå«ã¾ãã¦ãã¾ããããã¦ãã«ãã¼ã®å®ç¾©ãè¦ãã¨ãå é¨ã«ã¯IAsyncStateMachine m_stateMachine
ã¨ãããã£ã¼ã«ããåå¨ãã¾ãããã«ãã¼ã¯ããã¯ã¹åãããã¹ãã¼ããã·ã³ãåç §ããå¿ è¦ãããã¾ããããã«ããããã®å¾ã®ãµã¹ãã³ãæã«ããã§ã«ã¹ãã¼ããã·ã³ãããã¯ã¹åãããã¨ãããããå度ããã¯ã¹åããå¿ è¦ãããã¾ãããããããå ã»ã©ã¹ãã¼ããã·ã³ãããã¯ã¹åãã¾ãããããã«ãã¼ã«å«ã¾ããã¹ãã¼ããã·ã³ã§ããm_stateMachine
ãã£ã¼ã«ã㯠null ã§ãããã®ããã¯ã¹åãããã¹ãã¼ããã·ã³ã«å«ã¾ãããã«ãã¼ã®m_stateMachine
ã夿´ããé©åãªã¹ãã¼ããã·ã³ãæãããã«ããå¿ è¦ãããã¾ãããããå®ç¾ãããããã³ã³ãã¤ã©ãçæããã¹ãã¼ããã·ã³æ§é ä½ãå®è£ ããIAsyncStateMachine
ã¤ã³ã¿ã¼ãã§ã¼ã¹ã«ã¯ãvoid SetStateMachine(IAsyncStateMachine stateMachine)
ã¡ã½ãããå®ç¾©ããã¦ãã¾ã(åè: https://gist.github.com/nenoNaninu/41f8fa066dfd7dd7ef5377e2a2816747)ã ããã§ããã«ãã¼ã¯ã¾ãã¹ãã¼ããã·ã³ãããã¯ã¹åãããã®ããã¯ã¹åããããã®ãããã¯ã¹åããããªãã¸ã§ã¯ãã®SetStateMachine
ã¡ã½ããã«æ¸¡ãããã«ãã¼ã®SetStateMachine
ã¡ã½ãããå¼ã³åºãããã®ããã¯ã¹ããã£ã¼ã«ãã«æ ¼ç´ãã¾ãã - æå¾ã«ãcontinuation ã表ã
Action
ããªã²ã¼ããç¨æããawaiter ã®UnsafeOnCompleted
ã¡ã½ããã«æ¸¡ãã¾ããTaskAwaiter
ã®å ´åãtask ã¯ãã®Action
ããªã²ã¼ãã task ã® continuation ãªã¹ãã«æ ¼ç´ããtask ãå®äºããã¨ãAction
ããªã²ã¼ããå¼ã³åºããMoveNextRunner.Run
ãå¼ã³åºããExecutionContext.Run
ãå¼ã³åºããæå¾ã«ã¹ãã¼ããã·ã³ã®MoveNext
ã¡ã½ãããå¼ã³åºããã¹ãã¼ããã·ã³ããµã¹ãã³ãããã¨ããããå®è¡ãã¾ãã
ãã㯠.NET Framework ä¸ã§èµ·ãããã¨ã§ããããã¡ã¤ã©ã§ãã®çµæãè¦ãäºãã§ãã¾ãã ä¾ãã°ãallocation profiler ãå®è¡ãã¦ãå await ã§ä½ã allocation ããã¦ããããè¦ããã¨ãã§ãã¾ãã 以ä¸ã®ç¡çºãªããã°ã©ã ãè¦ã¦ã¿ã¾ãããã (以ä¸ã®ä¾ã¯ãé¢ä¿ãã allocation cost ã強調ããããã ãã«æ¸ãããã®ã§ãã)
using System.Threading; using System.Threading.Tasks; class Program { static async Task Main() { var al = new AsyncLocal<int>() { Value = 42 }; for (int i = 0; i < 1000; i++) { await SomeMethodAsync(); } } static async Task SomeMethodAsync() { for (int i = 0; i < 1000; i++) { await Task.Yield(); } } }
ãªããããªã«ã allocation ãçºçãã¦ããã®ã§ãããï¼åå ã¯å¹¾ã¤ãããã¾ãã
ExecutionContext
ããã㯠100ä¸åä»¥ä¸ allocation ãçºçãã¦ã¾ãããªãã§ãããï¼ãã㯠.NET Framework ã§ã¯ãExecutionContext
㯠ãã¥ã¼ã¿ãã«ãªãã¼ã¿æ§é ã ããã§ããéåæå¦çããã©ã¼ã¯ãããæç¹ã§åå¨ãã¦ãã ambient data ã¯ããã¼ãããããããã©ã¼ã¯å¾ã«è¡ããã夿´ã«ã¯å½±é¿ãããããªããExecutionContext
ãæ¯åã³ãã¼ããå¿ è¦ãããã¾ãããã©ã¼ã¯ãããæä½ã®ä¸ã¤ä¸ã¤ã«ãã®ãããªã³ãã¼ãå¿ è¦ãªã®ã§ãSomeMethodAsync
ã 1000å å¼ã³åºãã¨ãããããã 1000å ãµã¹ãã³ã/ãªã¸ã¥ã¼ã ããã®ã§ãåè¨ã§ 100ä¸å (1000 x 1000) ã®ExecutionContext
ãªãã¸ã§ã¯ãã«ãªãã¾ããã¤ããã§ãã...ãAction
ãããã100ä¸åä»¥ä¸ allocation ãçºçãã¦ã¾ããã¾ã å®äºãã¦ããªããã®ãå¾ ã¤ãã³ã«ããã® awaiter ã®UnsafeOnCompleted
ã¡ã½ããã«æ¸¡ãããã«æ°ããAction
ããªã²ã¼ãã allocate ãããã¨ã«ãªãã¾ããMoveNextRunner
ããµã¹ãã³ããããã³ã«æ°ããMoveNextRunner
ã allocate ãã¦ãAction ã¨ExecutionContext
ãä¿åããããããé©åã«å®è¡ããããã«ãã¦ããã®ã§ããã¯ãããã 100ä¸åã® allocation ãçºçãã¾ããLogicalCallContext
ãããã 100ä¸å allocation ãçºçãã¦ãã¾ããLogicalCallContext
ã¯ã.NET Framework ã®AsyncLocal<T>
ã®å®è£ ã®è©³ç´°ã§ãããExecutionContext
ã®ä¸é¨ã¨ãã¦ä¿åããã¦ãã¾ããã¤ã¾ããExecutionContext
ã®ã³ãã¼ã100ä¸åä½ã£ã¦ããã¨ãLogicalCallContext
ã®ã³ãã¼ã100ä¸åä½ã£ã¦ãããã¨ã«ãªãã¾ããQueueUserWorkItemCallback
ãåTask.Yield()
ã¯ã¹ã¬ãããã¼ã«ã« work item ã queueing ãã¦ããããã®çµæã100ä¸åã®æä½ã表ç¾ããããã«ä½¿ç¨ããã work item ãªãã¸ã§ã¯ãã® allocation ã100ä¸åçºçãã¾ããTask<VoidResult>
ãããã¯ä»ã¾ã§ã¿ã¦ãããã¤ã¨ã¯ç°ãªãã100ä¸å allocation ãçºçãã¦ããããã§ã¯ããã¾ãããéåæã«å®äºãã task å¼ã³åºãã¯ãã¹ã¦ããã®å¼ã³åºãã®æçµçãªå®äºã表ãããã«æ°ãã task ã allocate ããå¿ è¦ãããã¾ãã<SomeMethodAsync>d__1
. ããã¯ãã³ã³ãã¤ã©ãçæããã¹ãã¼ããã·ã³æ§é ä½ã®ããã¯ã¹ã§ãã 1000åã®ã¡ã½ããããµã¹ãã³ããã1000åã®ããã¯ã¹åãçºçãã¾ããQueueSegment/IThreadPoolWorkItem[]
ããããã¯æ°åå allocation ããã¦ãããæè¡çã«ã¯ç¹ã« async ã¡ã½ããã¨ã¯é¢ä¿ãªãããããä¸è¬çã«ã¹ã¬ãããã¼ã«ã«ãã¥ã¼ã¤ã³ã°ããã使¥ã¨é¢é£ãã¦ãã¾ãã.NET Framework ã§ã¯ãã¹ã¬ãããã¼ã«ã®ãã¥ã¼ã¯é循ç°é£çµãªã¹ãã§ããé·ãNã®ã»ã°ã¡ã³ãã«å¯¾ãã¦ãNåã®ã¯ã¼ã¯ã¢ã¤ãã ããã®ã»ã°ã¡ã³ãã« enqueue ãããdequeue ãããã¨ãã»ã°ã¡ã³ãã¯ç ´æ£ããããããã¬ãã¼ã¸ã³ã¬ã¯ã·ã§ã³ã®åå対象ã¨ãªãã¾ãã
対ãã¦ã.NET Core ã ã¨ããï¼
å§åçï¼å§åçæ¹åã§ããï¼ï¼
.NET Framework ã®ãã®ãµã³ãã«ã§ã¯ã500ä¸ä»¥ä¸ã® allocation ããããåè¨ã§ ï½145MB ã®ã¡ã¢ãªã使ããã¦ãã¾ããã 䏿¹ã§ .NET Core ã®åããµã³ãã«ã§ã¯ãããã ï½1000 ã® allocation ããããåè¨ ï½109KB ããã¡ã¢ãªã使ããã¦ãã¾ããããªããããªã«å°ãªãã®ã§ããããï¼
ExecutionContext
ã.NET Coreã§ã¯ãExecutionContext
ã immutable ã«ãªãã¾ããã ãã®æ¬ ç¹ã¯ãAsyncLocal<T>
ã«å¤ãã»ãããããªã©ãã³ã³ããã¹ãã夿´ãããã³ã«ã æ°ããExecutionContext
ã® allocation ãçºçããäºã§ãã ãããã³ã³ããã¹ããããã¼ããããã¨ã¯ãããã夿´ãããã¨ãããé¥ãã«ä¸è¬çã§ãããExecutionContext
ãä¸å¤ã«ãªã£ãã®ã§ãã³ã³ããã¹ããããã¼ããäºã®ä¸é¨ã¨ãã¦ExecutionContex
ã clone ããå¿ è¦ããªããªã£ãã¨ãããã¨ã§ããã³ã³ããã¹ãããã£ããã£ããã¨ãããã¨ã¯ãæåéããã£ã¼ã«ãããã³ã³ããã¹ããèªã¿åºããã¨ã§ãããã³ã³ããã¹ããèªã¿åºãã¦ãã®å 容ã®ã¯ãã¼ã³ã使ãããã¨ã§ã¯ããã¾ãããã¤ã¾ãã夿´ãããããããã¼ãããæ¹ãé¥ãã«ä¸è¬çã§ããã ãã§ãªããé¥ãã«å®ä¾¡ãªã®ã§ããLogicalCallContext
ãããã¯ãã¯ã .NET Core ã«ã¯åå¨ããªããªãã¾ããã .NET Coreã§ã¯ExecutionContext
ãåå¨ããã®ã¯AsyncLocal<T>
ã®ã¹ãã¬ã¼ã¸ã®ããã ãã§ãã .NET Framework ã§ExecutionContext
ã«ç¬èªã®ç¹å¥ã®æ ¼ç´å ´æãåå¨ããä»ã®ãã®ã¯ãAsyncLocal<T>
ã使ããããªå½¢ã§ã¢ãã«åããã¦ãã¾ãããã¨ãã°ã.NET Framework ã§ã¯SecurityContext
ã¯ExecutionContext
ã®ä¸é¨ã§ãããã.NET Core ã§SecurityContext
ã¯ãªããªããAsyncLocal<SafeAccessTokenHandle>
ã«ãªã£ãããã¾ãããQueueSegment/IThreadPoolWorkItem[]
ã.NET Coreã§ã¯ãThreadPool ã® global queue ã¯ConcurrentQueue<T>
ã¨ãã¦å®è£ ãããConcurrentQueue<T>
ã¯éåºå®é·ã®å¾ªç°é£çµãªã¹ãã«æ¸ãç´ããã¾ããããã®ããã»ã°ã¡ã³ãã®ãµã¤ãºãååã«å¤§ãããªããã»ã°ã¡ã³ããåã¾ããã¨ããªããªãã°ã追å ã®ã»ã°ã¡ã³ãã allocate ããå¿ è¦ã¯ãªããååã«å¤§ããã»ã°ã¡ã³ãããã å»¶ã ã¨ä½¿ãã¾ãããã¨ã«ãªãã¾ãã
ããããã¾ã è¬ã¯æ®ãã¾ãã
ãããã¡ã¤ã©ãè¦ãéããawaiter ã« continuation ã¨ãã¦æ¸¡ãããã® Action
ããªã²ã¼ãããMoveNextRunner
ã<SomeMethodAsync>d__1
ã® boxing ãªã©ã§çºçããã¯ãã® allocation ã¯ã©ããªã£ãã®ã§ããããï¼
ãããã® allocation ãã©ã®ããã«æ¶ããããçè§£ããã«ã¯ãç¾å¨ã® .NET Core ã§ã©ã®ããã«åä½ãã¦ããããç¥ãå¿
è¦ãããã¾ãã
ããã§ããµã¹ãã³ãæã«ä½ãèµ·ãã¦ããããæ¯ãè¿ã£ã¦ã¿ã¾ãããã
if (!awaiter.IsCompleted) // we need to suspend when IsCompleted is false { <>1__state = 1; <>u__2 = awaiter; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); return; }
ããã§çæãããã³ã¼ã(IL)㯠.NET Framework ã .NET Core ãã«é¢ãããåããã®ã§ãã
ããããAwaitUnsafeOnCompleted
ã¡ã½ããã®å®è£
㯠.NET Framework 㨠.NET Core ã§å¤§ããç°ãªã£ã¦ãã¾ãã
- .NET Framework / .NET Core ã®åæ¹ã¨ãæåã¯åãã§ã
ExecutionContext.Capture()
ã§ç¾å¨ã® ExecutionContext ãåå¾ãã¾ãã - ããã¦ãããã .NET Framework 㨠.NET Core ã§å¦çãç°ãªã£ã¦ãã¾ãã
.NET Core ã®ãã«ãã¼ã«ã¯ããã£ãä¸ã¤ã®ãã£ã¼ã«ããããã ãã§ãã
// .NET Core ã®ãã«ãã¼ public struct AsyncTaskMethodBuilder { private Task<VoidTaskResult>? m_task; // ~~ ç¥ ~~ }
ExecutionContext
ããã£ããã£ããå¾ãm_task
ãã£ã¼ã«ãã« AsyncStateMachineBox<TStateMachine>
(TStateMachine
ã¯ã³ã³ãã¤ã©ãçæããã¹ãã¼ããã·ã³æ§é ä½ã®å)ã®ãªãã¸ã§ã¯ããããããã§ãã¯ããã
ãã®AsyncStateMachineBox<TStateMachine>
åãããã¸ãã¯ãã§ãããæ¬¡ã®ããã«å®ç¾©ããã¦ãã¾ãã
private class AsyncStateMachineBox<TStateMachine> : Task<TResult>, IAsyncStateMachineBox where TStateMachine : IAsyncStateMachine { private Action? _moveNextAction; public TStateMachine? StateMachine; public ExecutionContext? Context; ... }
AsyncStateMachineBox<TStateMachine>
ã¯ãã®åã®éããã¹ã¿ãã¯ã«åå¨ããæ§é ä½ããã¼ãã« box åããããã®ã¯ã©ã¹ã§ãã
éè¦ãªäºã¯ãã¹ãã¼ããã·ã³ã IAsyncStateMachine
ã¨ã㦠boxing ããã®ã§ã¯ãªããTStateMachine
ã¨ãã¦å¼·ãåä»ãããããã£ã¼ã«ãã¨ãã¦ãã¼ãã«ã³ãã¼ããç¹ã§ãã
ããã¦ãå¥ã® Task
ãªãã¸ã§ã¯ããæã¤ã®ã§ã¯ãªããããã Task
ãªãã¸ã§ã¯ããã®ãã®ã§ã(ãã¼ã¹ã¯ã©ã¹ã«æ³¨ç®ãã¦ãã ããï¼Task<TResult>
ãç¶æ¿ãã¦ãã¾ãï¼)ã
ã¾ããAction
㨠ExecutionContext
ã®ä¸¡æ¹ãæ ¼ç´ããããã«å¥ã® MoveNextRunner
ãæã¤ã®ã§ã¯ãªãããã®åã®ãã£ã¼ã«ãã使ç¨ããã ãã§ãã
ExecutionContext
ã夿´ããã¦ããæ°ããã³ã³ããã¹ãã§ãã£ã¼ã«ãã䏿¸ãããã°ããã ããªã®ã§ãä»ã«ä½ã allocation ã¯çºçãã¾ããã
ãã㦠Action
ã¯ä¸åº¦ä½æãã¦ãã¾ãã°ãé©åãªå ´æãæãç¶ããã®ã§ãã£ãã·ã¥ãã¦ãããã¨ãåºæ¥ã¾ãã
ã¤ã¾ã ExecutionContext
ããã£ããã£ããå¾ããã§ã«AsyncStateMachineBox<TStateMachine>
ã®ãªãã¸ã§ã¯ããããã°ã ãã®ã¡ã½ããããµã¹ãã³ããããã®ã¯åãã¦ã§ã¯ãªãã®ã§ãæ°ãããã£ããã£ãã ExecutionContext
ããã£ã¼ã«ãã«æ ¼ç´ããã°ããã ãã§ããããã¾ã null ãªãã°ãAsyncStateMachineBox<TStateMachine>
ã allocate ããå¿
è¦ãããã¾ãã
// https://github.com/dotnet/runtime/blob/v7.0.5/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncTaskMethodBuilderT.cs#L152-L240 public struct AsyncTaskMethodBuilder<TResult> { private static IAsyncStateMachineBox GetStateMachineBox<TStateMachine>( ref TStateMachine stateMachine, [NotNull] ref Task<TResult>? taskField) where TStateMachine : IAsyncStateMachine { // æç² // ããããå®è£ ããã£ã¦ var box = new AsyncStateMachineBox<TStateMachine>(); // ããã§ allocate taskField = box; // important: this must be done before storing stateMachine into box.StateMachine! box.StateMachine = stateMachine; box.Context = currentContext; } // ~~ ç¥ ~~ }
ä¸è¨ã®ã½ã¼ã¹ã³ã¼ãã§ãimportantãã¨ãã¦ããè¡ã«æ³¨ç®ãã¦ãã ããã
ããã¯ã.NET Framework ã®è¤é㪠SetStateMachine
ãã³ã¹ã®ä»£ããã«ãªããã®ã§ã.NET Core ã§ã¯ SetStateMachine
ã¯å
¨ã使ç¨ããã¾ããã
box
ã代å
¥ããã¦ãã taskField
ã¯ãAsyncTaskMethodBuilder
ã® m_task
ãã£ã¼ã«ãã¸ã®åç
§ã§ãã
æã
㯠AsyncStateMachineBox<TStateMachine>
ã allocate ããtaskField
ãéãã¦ãã«ãã¼(ã¹ã¿ãã¯ä¸ã«ããã¹ãã¼ããã·ã³æ§é ä½ã«å«ã¾ãããã«ãã¼)ã® m_task
ã« box ãä¿åããããã¦
ãã®ã¹ã¿ãã¯ä¸ã«ããã¹ãã¼ããã·ã³(box ã¸ã®åç
§ããã§ã«å«ã)ãããã¼ãä¸ã® AsyncStateMachineBox<TStateMachine>
ã«ã³ãã¼ãã¾ãããã®ããã«ããäºã§ãAsyncStateMachineBox<TStateMachine>
ã¯é©åãã¤å帰çã«ãèªèº«ãåç
§ããããã«ãªãã¾ãã
ããã§ãã¾ã é£ããã§ãããããã§ã .NET Framework ã®è¤é㪠SetStateMachine
ãã³ã¹ã«æ¯ã¹ãã°ã ãã¶è¯ããªãã¾ããã
- ããã¦ã
StateMachine
ã®MoveNext
ãå¼ã³åºãåã«ãé©åãªExecutionContext
ã®å¾©å ãè¡ãAsyncStateMachineBox<TStateMachine>
ã®MoveNext
ã¡ã½ãããå¼ã³åºãããã®ãªãã¸ã§ã¯ãã®ã¡ã½ããã¸ã®Action
ããªã²ã¼ããåå¾ãã¾ãããã®Action
ã¯_moveNextAction
ãã£ã¼ã«ãã«ãã£ãã·ã¥ããããã以éã«ä½¿ç¨ããå ´åã¯ååã«ãã£ãã·ã¥ããAction
ã§ãã_moveNextAction
ãåå©ç¨ãããã¨ãã§ããããã«ãªãã¾ãããã®Action
㯠awaiter ã®UnsafeOnCompleted
ã«æ¸¡ãããcontinuation ãããã¯ãã¾ãã
private class AsyncStateMachineBox<TStateMachine> : Task<TResult>, IAsyncStateMachineBox where TStateMachine : IAsyncStateMachine { // ExecutionContext.RunInternal çã«ããªã²ã¼ããæããéã« // å¹ççã«è¡ãããã®ããªã²ã¼ãã®ãã£ãã·ã¥ private static readonly ContextCallback s_callback = ExecutionContextCallback; private static void ExecutionContextCallback(object? s) { ((AsyncStateMachineBox<TStateMachine>)s).StateMachine!.MoveNext(); } internal sealed override void ExecuteFromThreadPool(Thread threadPoolThread) => MoveNext(threadPoolThread); public void MoveNext() => MoveNext(threadPoolThread: null); private void MoveNext(Thread? threadPoolThread) { ExecutionContext? context = Context; if (context == null) { Debug.Assert(StateMachine != null); StateMachine.MoveNext(); } else { // MoveNext ã®å¼ã³æ¹ã«å·®ã¯ããã©ãã©ã¡ãã«ã // ExecutionContext ã復å ãã¦ããå®è¡ if (threadPoolThread is null) { // s_callback 㯠StateMachine.MoveNext() ãèªãã§ããã ãã ExecutionContext.RunInternal(context, s_callback, this); } else { ExecutionContext.RunFromThreadPoolDispatchLoop(threadPoolThread, context, s_callback, this); } } } public Action MoveNextAction => (Action)(_moveNextAction ??= new Action(MoveNext)); // ~~ ç¥ ~~ }
ä¸è¨ã®å
容ã ã¨ã<SomeMethodAsync>d__1
ã¯ããã¯ã¹åããããAsyncStateMachineBox<TStateMachine>
ã®ãã£ã¼ã«ã(Task
ããèªä½ã®ãã£ã¼ã«ãã¨ãããã)ã¨ãã¦åå¨ããããã«ãªããMoveNextRunner
㯠Action
㨠ExecutionContext
ãä¿åããããã ãã«åå¨ãã¦ãããã AsyncStateMachineBox<TStateMachine>
ã®åå¨ã®ãé°ã§ä¸è¦ã«ãªãã¾ããã
ãããããã®èª¬æããããã¨ãawaiter ã«æ¸¡ã _moveNextAction
ãå¿
è¦ãªããã1åã®ã¡ã½ããå¼ã³åºããã¨ã«ã1000åã® Action
ã® allocation ãçºçãã¦ããã¯ãã§ãã
ãããããããã¡ã¤ã©ã«ã¯ããã表示ããã¦ãã¾ããã
ãªãã§ãããã?
ã¾ããQueueUserWorkItemCallback
ãªãã¸ã§ã¯ãã«ã¤ãã¦ã¯ã©ãã§ããããã
Task.Yield()
ã®ä¸é¨ã¨ã㦠queueing ãã¦ããã®ã«ããªããããã¡ã¤ã©ã«è¡¨ç¤ºãããªãã®ã§ããããã
åè¿°ã®ããã«ãå®è£
ã®è©³ç´°ãã³ã¢ã©ã¤ãã©ãªã«æ¼ãåºããã¨ã®è¯ãç¹ã®1ã¤ã¯ãæéã®çµéã¨ã¨ãã«å®è£
ãé²åããããã¨ãã§ãããã¨ã§ã.NET Framework ãã .NET Core ã¸ã®é²åã¯ãã§ã«è¦ã¦ããã¨ããã§ãã
ã¾ãã.NET Core ã¸ã®æåã®æ¸ãæãããããã«é²åããã·ã¹ãã å
ã®ä¸»è¦ãªã³ã³ãã¼ãã³ãã«å
é¨ã¢ã¯ã»ã¹ã§ãããã¨ã«ããã¡ãªãããçãããæé©åã追å ããã¦ãã¾ãã
ç¹ã«ãéåæã¤ã³ãã©ã¹ãã©ã¯ãã£ã¯ãTask
ã TaskAwaiter
ãªã©ã®ã³ã¢ã¨ãªãåã«ã¤ãã¦ç¥ã£ã¦ãã¾ãã
ç¥ã£ã¦ããä¸ã«ãå
é¨ã¢ã¯ã»ã¹æ¨©ãæã£ã¦ãããããpublic ã«å®ç¾©ãããã«ã¼ã«ã«å¾ãå¿
è¦ãããã¾ããã
public ã«å®ç¾©ãããã«ã¼ã«ä¸ãC# ã® awaiter ãã¿ã¼ã³ã§ã¯ãawaiter ã« OnCompleted
ã¾ã㯠UnsafeOnCompleted
ã¡ã½ãããè¦æ±ããã©ã¡ãã continuation ã Action ã¨ãã¦åãåãã¾ãã
ã¤ã¾ããä»»æã® awaiter ãæ£ããåä½ããããã«ã¯ãéåæã¤ã³ãã©ã¹ãã©ã¯ãã£ã¯ continuation ã表ã Action
ããªã²ã¼ãã使ããå¿
è¦ãããã¾ãã
ããããããã¤ã³ãã©ã¹ãã©ã¯ãã£ãç¥ã£ã¦ãã awaiter ã«åºä¼ã£ãå ´åã¯ãåãã³ã¼ããã¹ãåã義åã¯ããã¾ããã
System.Private.CoreLib
ã§å®ç¾©ããã¦ãããã¹ã¦ã® awaiter ã¯ãAction
ãå
¨ãå¿
è¦ã¨ããªããããç¡é§ã®ãªãçµè·¯ãã¨ããã¨ãã§ãã¾ãã
ãããã® awaiter ã¯ãã¹ã¦ IAsyncStateMachineBox
ã«ã¤ãã¦ç¥ã£ã¦ãããbox ãªãã¸ã§ã¯ããã®ãã®ã continuation ã¨ãã¦åãæ±ããã¨ãã§ããã®ã§ãã
ä¾ãã°ãTask.Yield()
ãè¿ã YieldAwaitable
ã¯ãIAsyncStateMachineBox
èªä½ã work item ã¨ãã¦ã¹ã¬ãããã¼ã«ã«ç´æ¥ãã¥ã¼ã¤ã³ã°ãããã¨ãã§ããTask
ãªãã¸ã§ã¯ãã await ããã¨ãã«ä½¿ã TaskAwaiter
ã¯ãIAsyncStateMachineBox
èªä½ãç´æ¥ task ã® continuation ãªã¹ãã«æ ¼ç´ã§ããããã«ãªã£ã¦ãã¾ãã Action
ã¯å¿
è¦ãªããQueueUserWorkItemCallback
ãå¿
è¦ããã¾ããã
public readonly struct YieldAwaitable { // ~~ ç¥ ~~ public YieldAwaiter GetAwaiter() { return default; } public readonly struct YieldAwaiter : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter { // ~~ ç¥ ~~ // UnsafeOnCompleted ã« Action ãæ¸¡ãã®ã§ã¯ãªãã // AwaitUnsafeOnCompleted ã§ç´æ¥ box ãåãåããããã«ãªã£ã¦ããã void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) { Debug.Assert(box != null); // If tracing is enabled, delegate the Action-based implementation. if (TplEventSource.Log.IsEnabled()) { QueueContinuation(box.MoveNextAction, flowContext: false); return; } // Otherwise, this is the same logic as in QueueContinuation, except using // an IAsyncStateMachineBox instead of an Action, and only for flowContext:false. SynchronizationContext? syncCtx = SynchronizationContext.Current; if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext)) { syncCtx.Post(static s => ((IAsyncStateMachineBox)s!).MoveNext(), box); } else { TaskScheduler scheduler = TaskScheduler.Current; if (scheduler == TaskScheduler.Default) { // public ã«ãªã£ã¦ãã ThreadPool.QueueUserWorkItem ã§ã¯ããªã²ã¼ããæ¸¡ãã®ãæ®éã // 䏿¹ã§å é¨ã§ã¯ãThreadPool ã« ããªã²ã¼ãã§ã¯ãªã box ãç´æ¥æãè¾¼ããããã«ãªã£ã¦ããã // ãã®ãããã§ãããªã²ã¼ãã® allocation ãé²ãäºãåºæ¥ã¦ããã ThreadPool.UnsafeQueueUserWorkItemInternal(box, preferLocal: false); } else { Task.Factory.StartNew(static s => ((IAsyncStateMachineBox)s!).MoveNext(), box, default, TaskCreationOptions.PreferFairness, scheduler); } } } } }
public struct AsyncTaskMethodBuilder<TResult> { // ~~ ç¥ ~~ internal static void AwaitUnsafeOnCompleted<TAwaiter>( ref TAwaiter awaiter, IAsyncStateMachineBox box) where TAwaiter : ICriticalNotifyCompletion { // æ¢ç¥ã® awaiter ã«ã¤ãã¦ã¯ä»¥ä¸ã®ãããªæé©åãè¡ããã¦ããã // ã¡ãªã¿ã« null != (object?)default(TAwaiter) 㯠TAwaiter ãæ§é ä½ãã®æéãã§ãã¯ææ³ // ITaskAwaiter ç㯠internal 㪠marker interface. // is ã使ã£ã interface ã®å®è£ ãã§ãã¯ã JIT ææé©åã§æ¶ãã¦å®æ°ã«å¤ãã(boxing ã¨ãèµ·ããªã)ã if ((null != (object?)default(TAwaiter)) && (awaiter is ITaskAwaiter)) { ref TaskAwaiter ta = ref Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter); // relies on TaskAwaiter/TaskAwaiter<T> having the same layout TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, continueOnCapturedContext: true); } else if ((null != (object?)default(TAwaiter)) && (awaiter is IConfiguredTaskAwaiter)) { ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter ta = ref Unsafe.As<TAwaiter, ConfiguredTaskAwaitable.ConfiguredTaskAwaiter>(ref awaiter); TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, ta.m_continueOnCapturedContext); } else if ((null != (object?)default(TAwaiter)) && (awaiter is IStateMachineBoxAwareAwaiter)) { try { ((IStateMachineBoxAwareAwaiter)awaiter).AwaitUnsafeOnCompleted(box); } catch (Exception e) { System.Threading.Tasks.Task.ThrowAsync(e, targetContext: null); } } else { // The awaiter isn't specially known. Fall back to doing a normal await. try { awaiter.UnsafeOnCompleted(box.MoveNextAction); } catch (Exception e) { System.Threading.Tasks.Task.ThrowAsync(e, targetContext: null); } } } }
ãããã£ã¦ãasync ã¡ã½ããã System.Private.CoreLib
ç±æ¥ã®å (Task
, Task<TResult>
, ValueTask
, ValueTask<TResult>
, YieldAwaitable
, ConfigureAwait
ãªã©) ã®ãªãã¸ã§ã¯ãã await ããã¨ããæãä¸è¬çãªã±ã¼ã¹ã§ã¯ãææªã®ã±ã¼ã¹ã§ãasync ã¡ã½ããã®ã©ã¤ããµã¤ã¯ã«å
¨ä½ã«é¢é£ãããªã¼ãã¼ãããã1åã ã allocate ããããã¨ã«ãªãã¾ãã
ã¤ã¾ããasync ã¡ã½ããããµã¹ãã³ããããã¨ãããã°ãä»ã®ãã¹ã¦ã®å¿
è¦ãªç¶æ
ãæ ¼ç´ããåä¸ã® Task
æ´¾çåã allocate ãããã¡ã½ããã忢ããªããã°ã追å ã® allocation ãçºçãããã¨ã¯ããã¾ããã
ã¾ããå¿
è¦ã§ããã° amortized fashion(amortized fashion: pool ãã rent ã㦠return ãããããªã¹ã¿ã¤ã«)ãç¨ããäºã§ãæå¾ã® allocation ãåãé¤ããã¨ãã§ãã¾ãã
ããã¾ã§ç¤ºããããã«ãTask
ã«ã¯æ¢å®ã®ãã«ãã¼ (AsyncTaskMethodBuilder
) ããããåæ§ã« Task<TResult>
ã«ã¯æ¢å®ã®ãã«ãã¼ï¼AsyncTaskMethodBuilder<TResult>
ï¼ã ValueTask
㨠ValueTask<TResult>
ã«ã¯ ãããã AsyncValueTaskMethodBuilder
ãAsyncValueTaskMethodBuilder<TResult>
ã対å¿ãã¦åå¨ãã¾ãã
ValueTask
/ValueTask<TResult>
ã®å ´åããã«ãã¼èªä½ã¯åæçãã¤æ£å¸¸ã«å®äºããã±ã¼ã¹ã®ã¿ãå¦çãããããå®éã«ã¯é常ã«åç´ã§ãã
ãã®å ´åãasync ã¡ã½ããã¯ãµã¹ãã³ãããããã¨ãªãå®äºãããã«ãã¼ã¯ ValueTask.Completed
ãããã¯çµæãã©ãããã ValueTask<TResult>
ãè¿ãã ãã§æ¸ã¿ã¾ãã
ãã以å¤ã®å ´åã¯ãAsyncTaskMethodBuilder
/AsyncTaskMethodBuilder<TResult>
ã«å§è²ãããè¿ãããValueTask
/ValueTask<TResult>
㯠Task
/Task<TResult>
ãã©ãããã¦ããã ããªã®ã§ãåããã¸ãã¯ããã¹ã¦å
±æãã¦ãã¾ãã
ãããã.NET 6 㨠C# 10 ã§ã¯ãã¡ã½ãããã¨ã«ä½¿ç¨ãããã«ãã¼ããªã¼ãã¼ã©ã¤ãããæ©è½ãå°å
¥ãããValueTask
/ValueTask<TResult>
ç¨ã®ããã¤ãã®ç¹æ®ãªãã«ãã¼ãå°å
¥ããã¾ãããããã㯠Task
ã使ç¨ããã®ã§ã¯ãªããIValueTaskSource
/IValueTaskSource <TResult>
ãç¨ãã¦ãã¼ã«ãæ´»ç¨ãå®ç¾ãã¦ãã¾ãã
å®éã®ã³ã¼ããè¦ã¦ããã¾ãããã
ã¾ãã以ä¸ã®ãããªã³ã¼ãããã£ãã¨ãã¾ãã
static async ValueTask SomeMethodAsync() { for (int i = 0; i < 1000; i++) { await Task.Yield(); } }
ãã®æã以ä¸ã®ãããªã³ã¼ããã³ã³ãã¤ã©ã«ãã£ã¦çæããã¾ãã
// ããã©ã«ããªãããã [AsyncStateMachine(typeof(<SomeMethodAsync>d__1))] private static ValueTask SomeMethodAsync() { <SomeMethodAsync>d__1 stateMachine = default; stateMachine.<>t__builder = AsyncValueTaskMethodBuilder.Create(); stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; }
ããã§ãã¡ã½ããã«å¯¾ã㦠[AsyncMethodBuilder]
屿§ãã¤ãããã¨ã§ããã«ãã¼ãã«ã¹ã¿ãã¤ãºã§ãã¾ãã
[AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] // <- 追å static async ValueTask SomeMethodAsync() { for (int i = 0; i < 1000; i++) { await Task.Yield(); } }
ãã®å ´åã以ä¸ã®ãããªã³ã¼ããã³ã³ãã¤ã©ã«ãã£ã¦çæããã¾ãã ç¨ãããããã«ãã¼ãå¤ãã£ã¦ããäºã«æ³¨ç®ãã¦ãã ããã
[AsyncStateMachine(typeof(<SomeMethodAsync>d__1))] [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] private static ValueTask SomeMethodAsync() { <SomeMethodAsync>d__1 stateMachine = default; stateMachine.<>t__builder = PoolingAsyncValueTaskMethodBuilder.Create(); // <- æå®ãããã«ãã¼ã使ãããããã«ãªã£ã¦ãã stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; }
AsyncValueTaskMethodBuilder
㨠PoolingAsyncValueTaskMethodBuilder
ã®éè¦ãªéãã¯ãã¡ã½ãããæåã«ãµã¹ãã³ãããã¨ãã«ãnew AsyncStateMachineBox<TStateMachine>()
ãè¡ã代ããã« StateMachineBox<TStateMachine>.RentFromCache()
ãè¡ããasync ã¡ã½ãã (SomeMethodAsync
) ãçµäºãå®äºãã ValueTask
ãå¼ã³åºãå
ã«è¿ãããéã«ãRentFromCache()
ãã box ã ReturnToCache()
ãããã£ãã·ã¥ããåãã box ããã£ãã·ã¥ã«è¿å´ããäºã§ãã
ãã㯠(amortized) zero allocation ãæå³ãã¾ãã
ãã£ãã·ã¥èªä½ãã¡ãã£ã¨é¢ç½ããã®ã ã£ãããã¾ãã Object pooling ã¯ãè¯ãã¢ã¤ãã¢ã«ãæªãã¢ã¤ãã¢ã«ããªãå¾ã¾ãã 使ã«ã³ã¹ããããããªãã¸ã§ã¯ãã»ã©ããã¼ã«ãã価å¤ãé«ããªãã¾ãã ãããã£ã¦ããã¨ãã°ãé常ã«å°ããªé åããã¼ã«ããããããé常ã«å¤§ããªé åããã¼ã«ããæ¹ãã¯ããã«ä¾¡å¤ãããã¾ãã 大ããªé åã¯ãåæåããããã«å¤ãã®CPUãµã¤ã¯ã«ã¨ã¡ã¢ãªã¢ã¯ã»ã¹ãå¿ è¦ã¨ããã ãã§ãªããGC ã«è² æ ããããããã§ãã ããããé常ã«å°ããªãªãã¸ã§ã¯ãã®å ´åããã¼ã«ãããã¨ã¯å®è³ªçã«ãã¤ãã¹ã«ãªããã¨ãããã¾ãã ãã¼ã«ã¯ GC ã¨åæ§ãåãªã memory allocator ã§ãã ãã®ããããã¼ã«ããã¨ãã¯ãããã¢ãã±ã¼ã¿ã«é¢é£ããã³ã¹ãã¨å¥ã®ã¢ãã±ã¼ã¿ã«é¢é£ããã³ã¹ãããã¬ã¼ããªããããã¨ã«ãªãã¾ãã ã¾ã GC ã¯ã夿°ã®å°ãã寿å½ã®çããªãã¸ã§ã¯ããå¦çããéã«é常ã«å¹ççã§ãã ãªãã¸ã§ã¯ãã®ã³ã³ã¹ãã©ã¯ã¿ã§å¤ãã®å¦çãè¡ãå ´åããã¼ã«ããäºã§ãã®å¦çãåé¿ãããã¨ãã§ãããããpooling ã®ä¾¡å¤ãé«ã¾ãã¾ãã ãããããªãã¸ã§ã¯ãã®ã³ã³ã¹ãã©ã¯ã¿ã§å¤§ããå¦çãè¡ããã«ããããã¼ã«ããå ´åã¯ãGC ããããã¼ã«ã使ã£ãæ¹ãå¹ççã§ãããã¨ããä»®å®ã«åºã¥ãã¦ããã¯ããªããã§ãããå¤ãã®å ´åããã¯èª¤ã£ãçµæã«ãªãã¾ãã å ´åã«ãã£ã¦ã¯ãGC ã®ãã¥ã¼ãªã¹ãã£ãã¯ãªæé©åã«éãããã¨ã«ãªãããããã¾ããã ãã¨ãã°ãGC ã¯ããé«ãä¸ä»£(gen2)ã®ãªãã¸ã§ã¯ãããããä½ãä¸ä»£(gen0)ã®ãªãã¸ã§ã¯ãã¸ã®åç §ã¯æ¯è¼çå°ãªãããåæã«åºã¥ãã¦æé©åããã¦ãã¾ããããªãã¸ã§ã¯ãã® pooling ã¯ãããã®åæãç¡å¹ã«ããå¯è½æ§ãããã¾ãã ãã®ãã pooling ã«ãã£ã¦ããã¼ã¿ã«ã§ã®ããã©ã¼ãã³ã¹ã¯è½ã¡ãããããã¾ããã
ãã¦ãasync ã¡ã½ããã§çæããããªãã¸ã§ã¯ãã¯å°ããããã¾ããããã¹ã¼ãã¼ããããªãã¹ä¸ã«ãããã¨ãããã®ã§ããã¼ãªã³ã°ã¯åççãªå ´åãããã¾ãã
ãããããããå¯è½ãªéã価å¤ãããã®ã«ããããã«ãå¯è½ãªéããªã¼ãã¼ããããé¿ãããã¨ãèãã¦ãã¾ãã
ãã®ãããPoolingAsyncValueTaskMethodBuilder
ã§ã¯ã¢ã°ã¬ãã·ãã«ãã£ãã·ã¥ãå©ç¨ããäºã«æ¯ã¹ããããå¤ãã® allocate ãè¡ãå¯è½æ§ããã£ãã¨ãã¦ããé常ã«ã·ã³ãã«ã§ç«¶åãã»ã¨ãã©ãªãç¶æ
ã§é常ã«é«éã«ãªãã¸ã§ã¯ãã rent/return ãããããªãã¼ã«ãå®è£
ãã鏿ããã¦ãã¾ãã
ã¹ãã¼ããã·ã³ã®åãã¨ã«ãã¹ã¬ãããã¨ã«æå¤§ 1 ã¤ã®ã¹ãã¼ããã·ã³ããã¯ã¹ãCPU ã®ã³ã¢ãã¨ã« 1 ã¤ã®ã¹ãã¼ããã·ã³ããã¯ã¹ããã¼ã«ãã¾ãã
ããã«ãããæå°éã®ãªã¼ãã¼ãããã¨ç«¶åã§ãrent 㨠return ãè¡ããã¨ãã§ãã¾ãã
ããã¹ã¬ãããåæã«ä»ã®ã¹ã¬ããåºæã®ãã£ãã·ã¥ã«ã¢ã¯ã»ã¹ãããã¨ã¯ã§ãããä»ã®ã¹ã¬ãããåæã«ã³ã¢åºæã®ãã£ãã·ã¥ã«ã¢ã¯ã»ã¹ããäºã¯ç¨ã§ãã(ãã®ãããã®ã話㯠PoolingAsyncValueTaskMethodBuilder
ã® å®è£
ãèªãã æ¹ããããªãå
¥ã£ã¦ããããããã¾ãããä¸ã«æç²ãã¾ããããallocation ã忏ããããã®ã¢ã°ã¬ãã·ããªãã¼ã«(zero allocation ãæåãããã¼ã«)ããããé«éã« rent/return ããäºã鏿ãã¦ããã¨ããã®ãå¦å®ã«åããã¾ãã)
public struct PoolingAsyncValueTaskMethodBuilder<TResult> { // ~~ ç¥ ~~ private sealed class StateMachineBox<TStateMachine> : StateMachineBox, IValueTaskSource<TResult>, IValueTaskSource, IAsyncStateMachineBox, IThreadPoolWorkItem where TStateMachine : IAsyncStateMachine { // ~~ ç¥ ~~ [MethodImpl(MethodImplOptions.AggressiveInlining)] // only one caller internal static StateMachineBox<TStateMachine> RentFromCache() { // First try to get a box from the per-thread cache. StateMachineBox<TStateMachine>? box = t_tlsCache; if (box is not null) { t_tlsCache = null; } else { // If we can't, then try to get a box from the per-core cache. ref StateMachineBox<TStateMachine>? slot = ref PerCoreCacheSlot; if (slot is null || (box = Interlocked.Exchange<StateMachineBox<TStateMachine>?>(ref slot, null)) is null) { // If we can't, just create a new one. box = new StateMachineBox<TStateMachine>(); } } return box; } /// <summary>Returns this instance to the cache.</summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] // only two callers private void ReturnToCache() { // Clear out the state machine and associated context to avoid keeping arbitrary state referenced by // lifted locals, and reset the instance for another await. ClearStateUponCompletion(); _valueTaskSource.Reset(); // If the per-thread cache is empty, store this into it.. if (t_tlsCache is null) { t_tlsCache = this; } else { // Otherwise, store it into the per-core cache. ref StateMachineBox<TStateMachine>? slot = ref PerCoreCacheSlot; if (slot is null) { // Try to avoid the write if we know the slot isn't empty (we may still have a benign race condition and // overwrite what's there if something arrived in the interim). Volatile.Write(ref slot, this); } } } } }
ãªããããã¯æ¯è¼çå°ããªãã¼ã«ã®ããã«è¦ããããããã¾ãããããã¼ã«ãç¾å¨ä½¿ç¨ããã¦ããªããªãã¸ã§ã¯ãã®æ ¼ç´ã®ã¿ãæ å½ãã¦ããäºãèããã¨ãå®å¸¸ç¶æ (steady state) ã§ã® allocation ãå¤§å¹ ã«åæ¸ããã®ã«é常ã«å¹æçã§ãã
ãªããªããã¼ã«ã¯ç¾å¨ä½¿ç¨ããã¦ããªããªãã¸ã§ã¯ãã®ã¿ãæ ¼ç´ãã責任ãæã£ã¦ããããã§ãã ããã¯å½ããåã®ãã¨ã®ããã«æããããããã¾ããããé常ã«éè¦ãªäºã§ãã 100ä¸ã®éåæã¡ã½ããããã¹ã¦åæã«å®è¡ããã¦ããã¨ãã¦ãããã¼ã«ã¯ã¹ã¬ãããã¨ããã³ã³ã¢ãã¨ã«æå¤§1ã¤ã®ãªãã¸ã§ã¯ãããæ ¼ç´ã§ãã¾ããã ããã§ã allocation ãå¤§å¹ ã«åæ¸ããã®ã«é常ã«å¹æçã§ãã ãªããªãããªãã¸ã§ã¯ãã¯ãã operation ããå¥ã® operation ã¸ã®è»¢éã«å¿ è¦ãªæéã®ã¿ãã¼ã«ã«ä¿æãããå¿ è¦ããããbox ã operation ã«ãã£ã¦ä½¿ç¨ããã¦ããéã¯ãã¼ã«ã«ä¿æããå¿ è¦ããªãããã§ãã
ãããã«
ä¸çªã¯ããã«æ¸ããããã«ããã®è¨äºã¯ Stephen Toub æ°ã® How Async/Await Really Works in C# ã¨ããè¨äºããã¼ã¹ã«ãã¦ãã¾ãã æ¬å½ã«ç´ æ´ãããè¨äºã§ãç§ã¯ãããèªãã æããªããã£ããã£ãã¦ãã¾ããã ãããã¨ã Stephen æ°ã ããã¦ãã®è¨äºã®èªè ã C# ã® async/await ã«ã¤ãã¦çè§£ãæ·±ã¾ãã° C# æ¨ãã®ç§ã¨ãã¦ã幸ãã§ãã æ¥½ãã C# ã³ã¼ãã£ã³ã°ã©ã¤ããéã£ã¦ããã¾ããã...!