Avi*_*mar 0 c# actor async-await orleans .net-core
我有以下来自奥尔良的客户和谷物的代码片段。(虽然在 Orleans 中推荐的开发方式是等待任务,但以下代码在某些时候并不等待,纯粹是出于实验目的)
// client code
while(true)
{
Console.WriteLine("Client giving another request");
double temperature = random.NextDouble() * 40;
var grain = client.GetGrain<ITemperatureSensorGrain>(500);
Task t = sensor.SubmitTemperatureAsync((float)temperature);
Console.WriteLine("Client Task Status - "+t.Status);
await Task.Delay(5000);
}
// ITemperatureSensorGrain code
public async Task SubmitTemperatureAsync(float temperature)
{
long grainId = this.GetPrimaryKeyLong();
Console.WriteLine($"{grainId} outer received temperature: {temperature}");
Task x = SubmitTemp(temperature); // SubmitTemp() is another function in the same grain
x.Ignore();
Console.WriteLine($"{grainId} outer received temperature: {temperature} exiting");
}
public async Task SubmitTemp(float temp)
{
for(int i=0; i<1000; i++)
{
Console.WriteLine($"Internal function getting awaiting task {i}");
await Task.Delay(1000);
}
}
Run Code Online (Sandbox Code Playgroud)
当我运行上面的代码时,输出如下:
Client giving another request
Client Task Status - WaitingForActivation
500 outer received temperature: 23.79668
Internal function getting awaiting task 0
500 outer received temperature: 23.79668 exiting
Internal function getting awaiting task 1
Internal function getting awaiting task 2
Internal function getting awaiting task 3
Internal function getting awaiting task 4
Client giving another request
Client Task Status - WaitingForActivation
500 outer received temperature: 39.0514
Internal function getting awaiting task 0 <------- from second call to SubmitTemp
500 outer received temperature: 39.0514 exiting
Internal function getting awaiting task 5 <------- from first call to SubmitTemp
Internal function getting awaiting task 1
Internal function getting awaiting task 6
Internal function getting awaiting task 2
Internal function getting awaiting task 7
Internal function getting awaiting task 3
Internal function getting awaiting task 8
Internal function getting awaiting task 4
Internal function getting awaiting task 9
Run Code Online (Sandbox Code Playgroud)
从普通 .Net 应用程序的角度来看,输出是有意义的。如果我可以从这篇 stackoverflow 帖子中获得帮助,那么这里发生的事情是:
ITemperatureSendorGrain
并继续进行。当await
命中时,客户端线程将返回到线程池。SubmitTemperatureAsync
接收请求并调用本地异步函数SubmitTemp
。SubmitTemp
打印对应于 i=0 的语句,然后点击等待。Await 导致其余部分for loop
被安排为可等待 (Task.Delay) 的延续,并且控制权返回到调用函数SubmitTemperatureAsync
。这里需要注意的是,当线程在SubmitTemp
函数中遇到await时,并不会返回线程池。线程控制实际上返回给调用函数SubmitTemperatureAsync
。因此,turn
按照 Orleans 文档中的定义, a 会在顶级方法遇到等待时结束。当一轮结束时,线程返回到线程池。SubmitTemp
在1秒后返回时,它从线程池中获取一个线程并在其上调度其余的线程for loop
。for loop
计划与第二次调用相对应的另一轮SubmitTemp
。我的第一个问题是我是否正确描述了代码中发生的情况,特别是当在函数中点击await时,线程没有返回到线程池SubmitTemp
。
根据grain的单线程特性,任何时候只有一个线程会执行grain的代码。此外,一旦对grain的请求开始执行,它将在下一个请求被处理之前完全完成(chunk based execution
在奥尔良文档中称为)。从高层次上来说,上述代码确实如此,因为SubmitTemperatureAsync
只有当当前调用该方法退出时,才会发生下一次调用。
然而,SubmitTemp
实际上是 的子功能SubmitTemperatureAsync
。尽管SubmitTemperatureAsync
已退出,SubmitTemp
但仍在执行,并且在执行此操作时,Orleans 允许另一个调用来SubmitTemperatureAsync
执行。我的第二个问题是否违反了奥尔良谷物的单线程性质?
考虑到SubmitTemp
它for loop
需要访问grain类的一些数据成员。因此,ExecutionContext
当遇到await时,将被捕获,当Task.Delay(1000)
返回时,捕获的将被传递给线程上ExecutionContext
剩余部分的调度。for loop
因为ExecutionContext
已通过,所以for loop
尽管在不同的线程上运行,剩余部分仍将能够访问数据成员。这是任何普通 .Net 异步应用程序中都会发生的事情。
我的第三个问题是关于SynchronizationContext
. 我在 Orleans 存储库中进行了粗略搜索,但找不到 的任何实现SynchronizationContext.Post()
,这使我相信不需要SynchronizationContext
运行 Orleans 方法。谁能证实这一点吗?如果这不是真的,并且 a 是SynchronizationContext
必需的,那么并行执行 的各种调用SubmitTemp
(如上面的代码所示)是否会冒以死锁结束的风险(如果有人坚持并不这样做SynchronizationContext
)不释放它)?
在我看来,您的描述大致正确,但这里有一些要点:
TaskScheduler
。TaskScheduler
。await
未同步完成的命中,或者用户await
根本没有使用并且正在使用ContinueWith
自定义等待项进行编程。await SubmitTemp(x)
来代替.Ignoring()
it,则回合将在Task.Delay(...)
内部被击中时结束SubmitTemp(x)
。不,在给定时间只有一个线程执行grain的代码。然而,该“线程”必须在激活上安排的各种任务之间分配时间TaskScheduler
。即,永远不会有这样的情况:您挂起进程并发现两个线程同时执行您的grain 代码。
就运行时而言,当Task
从顶级方法返回(或其他可等待类型)完成时,消息的处理就会结束。在此之前,不会安排在您激活时执行任何新消息。从您的方法生成的后台任务始终允许与其他任务交错。
.NET 允许子任务附加到其父任务。在这种情况下,父任务仅在所有子任务完成后才完成。然而,这不是默认行为,通常建议您避免选择此行为(例如,通过传递TaskCreationOptions.AttachedToParent
到Task.Factory.StartNew
)。
如果您确实使用了该行为(请不要使用),那么您将在第一次调用时看到SubmitTemp()
无限期的激活循环,并且不会再处理任何消息。
SynchronizationContext
?奥尔良不使用SynchronizationContext
. 相反,它使用自定义TaskScheduler
实现。看ActivationTaskScheduler.cs
。每个激活都有其自己的ActivationTaskScheduler
,并且所有消息都是使用该调度程序的调度程序。
关于后续问题,针对Task
激活进行调度的实例(每个实例代表一项同步工作)被插入到同一队列中,因此允许它们交错,但ActivationTaskScheduler
一次只能由一个线程执行。
归档时间: |
|
查看次数: |
1926 次 |
最近记录: |