IAsyncStateMachine 如何管理 MethodBuilder 上的多个等待者?
How does IAsyncStateMachine manages multiple awaiters on the MethodBuilder?
我想了解这 4 个部分是如何组合在一起的:
IAwaiter
IAwaitable
IAsyncMethodBuilder
IAsyncStateMachine
我不明白 IAsyncMethodBuilder
等待者和状态机之间的关系。
如果我的方法生成器接收到让我们说 2 个等待者,为什么状态机在它的肚子里只有一个它使用 Get/Set 结果的等待者?
我说的是 methodbuilder 上的等待者:
var a= await MyMethod();
var b=await MyMethod();
其中 MyMethod
定义为:
async Task<T> MyMethod(){
await f1() ->don't care about thsese
await f2() -----/-----
await f3() -----/------
........
}
我会post迪欣博客的两段代码https://weblogs.asp.net/dixin/functional-csharp-asynchronous-function:
用户编写的代码:
internal static async Task<T> Async<T>(T value)
{
T value1 = Start(value);
T result1 = await Async1(value1);
T value2 = Continuation1(result1);
T result2 = await Async2(value2);
T value3 = Continuation2(result2);
T result3 = await Async3(value3);
T result = Continuation3(result3);
return result;
}
internal static T Start<T>(T value) => value;
internal static Task<T> Async1<T>(T value) => Task.Run(() => value);
internal static T Continuation1<T>(T value) => value;
internal static Task<T> Async2<T>(T value) => Task.FromResult(value);
internal static T Continuation2<T>(T value) => value;
internal static Task<T> Async3<T>(T value) => Task.Run(() => value);
internal static T Continuation3<T>(T value) => value;
编译器生成的内容:
[CompilerGenerated]
[StructLayout(LayoutKind.Auto)]
private struct AsyncStateMachine<TResult> : IAsyncStateMachine
{
public int State;
public AsyncTaskMethodBuilder<TResult> Builder;
public TResult Value;
private TaskAwaiter<TResult> awaiter; //Why only one?
void IAsyncStateMachine.MoveNext()
{
TResult result;
try
{
switch (this.State)
{
case -1: // Start code from the beginning to the 1st await.
// Workflow begins.
TResult value1 = Start(this.Value);
this.awaiter = Async1(value1).GetAwaiter();
if (this.awaiter.IsCompleted)
{
// If the task returned by Async1 is already completed, immediately execute the continuation.
goto case 0;
}
else
{
this.State = 0;
// If the task returned by Async1 is not completed, specify the continuation as its callback.
this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
// Later when the task returned by Async1 is completed, it calls back MoveNext, where State is 0.
return;
}
case 0: // Continuation code from after the 1st await to the 2nd await.
// The task returned by Async1 is completed. The result is available immediately through GetResult.
TResult result1 = this.awaiter.GetResult();
TResult value2 = Continuation1(result1);
this.awaiter = Async2(value2).GetAwaiter();
if (this.awaiter.IsCompleted)
{
// If the task returned by Async2 is already completed, immediately execute the continuation.
goto case 1;
}
else
{
this.State = 1;
// If the task returned by Async2 is not completed, specify the continuation as its callback.
this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
// Later when the task returned by Async2 is completed, it calls back MoveNext, where State is 1.
return;
}
case 1: // Continuation code from after the 2nd await to the 3rd await.
// The task returned by Async2 is completed. The result is available immediately through GetResult.
TResult result2 = this.awaiter.GetResult();
TResult value3 = Continuation2(result2);
this.awaiter = Async3(value3).GetAwaiter();
if (this.awaiter.IsCompleted)
{
// If the task returned by Async3 is already completed, immediately execute the continuation.
goto case 2;
}
else
{
this.State = 2;
// If the task returned by Async3 is not completed, specify the continuation as its callback.
this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
// Later when the task returned by Async3 is completed, it calls back MoveNext, where State is 1.
return;
}
case 2: // Continuation code from after the 3rd await to the end.
// The task returned by Async3 is completed. The result is available immediately through GetResult.
TResult result3 = this.awaiter.GetResult();
result = Continuation3(result3);
this.State = -2; // -2 means end.
this.Builder.SetResult(result);
// Workflow ends.
return;
}
}
catch (Exception exception)
{
this.State = -2; // -2 means end.
this.Builder.SetException(exception);
}
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine asyncStateMachine) =>
this.Builder.SetStateMachine(asyncStateMachine);
}
鉴于我在开头编写的示例,AsyncStateMachine
不应该有一个等待者列表吗?如果我在 methodbuilder 上有 N 个等待者,机器如何将 SetResult
传播给它的所有等待者?
状态机一次只能等待一个可等待的操作。可能涉及多个操作,但一次只能在一个等待点。因此,如果这些等待者是同一类型,则只需要一个字段来等待它。
如果同一个方法中有不同类型的等待者,我相信您会看到每种等待者类型一个字段。 (编译器可能会为所有等待者使用单个 object
字段,并在继续触发时将其适当地转换回去,但这会带来不同的问题,特别是当等待者是值类型时。)
这是一个例子:
using System;
using System.Threading.Tasks;
class Test
{
static async Task FooAsync()
{
await Bar<int>();
await Bar<string>();
await Task.Delay(1000);
await Bar<string>();
await Task.Yield();
}
static Task<T> Bar<T>() => Task.FromResult(default(T));
}
这里我们在以下状态机中得到等待者字段:
TaskAwaiter<int> <>u__1; // From the Bar<int> call
TaskAwaiter<string> <>u__2; // From both Bar<string> calls
TaskAwaiter <>u__3; // From Task.Delay
YieldAwaitable.YieldAwaiter <>u__4; // From Task.Yield
我想了解这 4 个部分是如何组合在一起的:
IAwaiter
IAwaitable
IAsyncMethodBuilder
IAsyncStateMachine
我不明白 IAsyncMethodBuilder
等待者和状态机之间的关系。
如果我的方法生成器接收到让我们说 2 个等待者,为什么状态机在它的肚子里只有一个它使用 Get/Set 结果的等待者?
我说的是 methodbuilder 上的等待者:
var a= await MyMethod();
var b=await MyMethod();
其中 MyMethod
定义为:
async Task<T> MyMethod(){
await f1() ->don't care about thsese
await f2() -----/-----
await f3() -----/------
........
}
我会post迪欣博客的两段代码https://weblogs.asp.net/dixin/functional-csharp-asynchronous-function:
用户编写的代码:
internal static async Task<T> Async<T>(T value)
{
T value1 = Start(value);
T result1 = await Async1(value1);
T value2 = Continuation1(result1);
T result2 = await Async2(value2);
T value3 = Continuation2(result2);
T result3 = await Async3(value3);
T result = Continuation3(result3);
return result;
}
internal static T Start<T>(T value) => value;
internal static Task<T> Async1<T>(T value) => Task.Run(() => value);
internal static T Continuation1<T>(T value) => value;
internal static Task<T> Async2<T>(T value) => Task.FromResult(value);
internal static T Continuation2<T>(T value) => value;
internal static Task<T> Async3<T>(T value) => Task.Run(() => value);
internal static T Continuation3<T>(T value) => value;
编译器生成的内容:
[CompilerGenerated]
[StructLayout(LayoutKind.Auto)]
private struct AsyncStateMachine<TResult> : IAsyncStateMachine
{
public int State;
public AsyncTaskMethodBuilder<TResult> Builder;
public TResult Value;
private TaskAwaiter<TResult> awaiter; //Why only one?
void IAsyncStateMachine.MoveNext()
{
TResult result;
try
{
switch (this.State)
{
case -1: // Start code from the beginning to the 1st await.
// Workflow begins.
TResult value1 = Start(this.Value);
this.awaiter = Async1(value1).GetAwaiter();
if (this.awaiter.IsCompleted)
{
// If the task returned by Async1 is already completed, immediately execute the continuation.
goto case 0;
}
else
{
this.State = 0;
// If the task returned by Async1 is not completed, specify the continuation as its callback.
this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
// Later when the task returned by Async1 is completed, it calls back MoveNext, where State is 0.
return;
}
case 0: // Continuation code from after the 1st await to the 2nd await.
// The task returned by Async1 is completed. The result is available immediately through GetResult.
TResult result1 = this.awaiter.GetResult();
TResult value2 = Continuation1(result1);
this.awaiter = Async2(value2).GetAwaiter();
if (this.awaiter.IsCompleted)
{
// If the task returned by Async2 is already completed, immediately execute the continuation.
goto case 1;
}
else
{
this.State = 1;
// If the task returned by Async2 is not completed, specify the continuation as its callback.
this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
// Later when the task returned by Async2 is completed, it calls back MoveNext, where State is 1.
return;
}
case 1: // Continuation code from after the 2nd await to the 3rd await.
// The task returned by Async2 is completed. The result is available immediately through GetResult.
TResult result2 = this.awaiter.GetResult();
TResult value3 = Continuation2(result2);
this.awaiter = Async3(value3).GetAwaiter();
if (this.awaiter.IsCompleted)
{
// If the task returned by Async3 is already completed, immediately execute the continuation.
goto case 2;
}
else
{
this.State = 2;
// If the task returned by Async3 is not completed, specify the continuation as its callback.
this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
// Later when the task returned by Async3 is completed, it calls back MoveNext, where State is 1.
return;
}
case 2: // Continuation code from after the 3rd await to the end.
// The task returned by Async3 is completed. The result is available immediately through GetResult.
TResult result3 = this.awaiter.GetResult();
result = Continuation3(result3);
this.State = -2; // -2 means end.
this.Builder.SetResult(result);
// Workflow ends.
return;
}
}
catch (Exception exception)
{
this.State = -2; // -2 means end.
this.Builder.SetException(exception);
}
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine asyncStateMachine) =>
this.Builder.SetStateMachine(asyncStateMachine);
}
鉴于我在开头编写的示例,AsyncStateMachine
不应该有一个等待者列表吗?如果我在 methodbuilder 上有 N 个等待者,机器如何将 SetResult
传播给它的所有等待者?
状态机一次只能等待一个可等待的操作。可能涉及多个操作,但一次只能在一个等待点。因此,如果这些等待者是同一类型,则只需要一个字段来等待它。
如果同一个方法中有不同类型的等待者,我相信您会看到每种等待者类型一个字段。 (编译器可能会为所有等待者使用单个 object
字段,并在继续触发时将其适当地转换回去,但这会带来不同的问题,特别是当等待者是值类型时。)
这是一个例子:
using System;
using System.Threading.Tasks;
class Test
{
static async Task FooAsync()
{
await Bar<int>();
await Bar<string>();
await Task.Delay(1000);
await Bar<string>();
await Task.Yield();
}
static Task<T> Bar<T>() => Task.FromResult(default(T));
}
这里我们在以下状态机中得到等待者字段:
TaskAwaiter<int> <>u__1; // From the Bar<int> call
TaskAwaiter<string> <>u__2; // From both Bar<string> calls
TaskAwaiter <>u__3; // From Task.Delay
YieldAwaitable.YieldAwaiter <>u__4; // From Task.Yield