У меня есть следующий фрагмент кода клиента и зерна в Орлеане. (Хотя рекомендуемый способ разработки в Орлеане - это дождаться Задачи, в некоторых местах следующий код не ожидает только для экспериментов)
// 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);
}
}
Вывод при запускеприведенный выше код выглядит следующим образом:
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
Вывод имеет смысл с точки зрения обычного приложения .Net.Если я могу получить помощь от этого сообщения о потоке стека , то, что здесь происходит, это то, что:
- Клиент звонит на
ITemperatureSendorGrain
и продолжает работу.При нажатии await
клиентский поток возвращается в пул потоков. SubmitTemperatureAsync
получает запрос и вызывает локальную асинхронную функцию SubmitTemp
. SubmitTemp
печатает оператор, соответствующий i = 0, после которого он достигает точки ожидания.Await приводит к тому, что остаток for loop
будет запланирован как продолжение ожидаемого (Task.Delay), и элемент управления вернется к вызывающей функции SubmitTemperatureAsync
.Обратите внимание, что поток не возвращается в пул потоков, когда он встречает await в функции SubmitTemp
.Управление потоком фактически возвращается к вызывающей функции SubmitTemperatureAsync
.Итак, turn
, как определено в документации Орлеана, заканчивается, когда метод верхнего уровня встречает ожидание.Когда ход заканчивается, поток возвращается в пул потоков. - Вызывающая функция не ожидает завершения задачи и завершается.
- Когда ожидаемое в
SubmitTemp
возвращается через 1 с, она получает поток из пула потоков и планирует остальныеfor loop
на нем. - Когда возвращается ожидаемое в клиентском коде, он делает еще один вызов с тем же зерном, и запланирован еще один раунд
for loop
, соответствующий второму вызову SubmitTemp
.
Мой первый вопрос - правильно ли я описал, что происходит в коде, особенно о том, что поток не возвращается в пул потоков при достижении ожидания в функции SubmitTemp
.
В соответствии с однопоточным характером зерен, в любой момент времени только один поток будет выполнять код зерна.Кроме того, как только запрос к зерну начинает выполняться, он будет полностью завершен до того, как будет принят следующий запрос (в документах Орлеана chunk based execution
).На высоком уровне это верно для приведенного выше кода, потому что следующий вызов SubmitTemperatureAsync
произойдет только при выходе из текущего вызова метода.
Однако SubmitTemp
на самом деле была подфункцией SubmitTemperatureAsync
.Хотя SubmitTemperatureAsync
завершено, SubmitTemp
все еще выполняется, и пока он это делает, Орлеан разрешил выполнить еще один вызов SubmitTemperatureAsync
.Разве это не нарушает однопоточный характер зерна Орлеана - это мой второй вопрос ?
Учтите, что SubmitTemp
в его for loop
требуется доступ к некоторым членам данныхкласс зерна.Таким образом, ExecutionContext
будет захвачено, когда ожидается ожидание, и когда Task.Delay(1000)
вернется, захваченное ExecutionContext
будет передано в планирование остатка for loop
в потоке.Поскольку ExecutionContext
пройдено, оставшаяся часть for loop
сможет получить доступ к элементам данных, несмотря на то, что работает в другом потоке.Это то, что происходит в любом обычном асинхронном приложении .Net.
Мой третий вопрос касается SynchronizationContext
.Я сделал краткий поиск в репозитории Орлеана, но не смог найти реализацию SynchronizationContext.Post()
, что заставляет меня поверить, что SynchronizationContext
не требуется для запуска методов Орлеана.Кто-нибудь может это подтвердить?Если это не так, и требуется SynchronizationContext
, не будет ли параллельное выполнение различных вызовов SubmitTemp
(как показано в приведенном выше коде) рисковать завершиться тупиком (если кто-то удерживаетна SynchronizationContext
и не выпускает его)?