В чем разница между ними?
Task.Run
начинает выполнение делегата в потоке пула потоков; прямой вызов метода запускает запуск делегата в текущем потоке .
При изучении async
полезно выделить все, чтобы вы могли точно видеть, что происходит:
entities = DoAnotherWork(entities).Result;
эквивалентен:
var entitiesTask = DoAnotherWork(entities);
entities = entitiesTask.Result;
и этот код:
Task.Run(async () => entities = await DoAnotherWork(entities)).Wait();
эквивалентен:
async Task LambdaAsMethod()
{
entities = await DoAnotherWork(entities);
}
var runTask = Task.Run(LambdaAsMethod);
runTask.Wait();
Какой код является подходящий и / или безопасный (= не вызовет взаимоблокировку)?
Вам следует избегать Task.Run
в среде ASP. NET, поскольку это будет мешать с обработкой ASP. NET пула потоков и принудительным переключением потоков, когда ничего не требуется.
В какой ситуации возникает тупик, если он есть?
Сценарий общего тупика требует двух вещей:
- Код, который блокирует асинхронный код вместо правильного использования
await
. - Контекст, который обеспечивает синхронизация (т. е. позволяет только один блок кода "в" контексте за один раз).
The лучшее решение - убрать первое условие; другими словами, используйте "async
полностью" . Чтобы применить это здесь, лучшим решением будет полное снятие блокировки:
public Task async ProcessAsync(IEnumerable<EventData> messages)
{
...
var entities = await ConvertToEntityAsync(messages);
...
}
public async Task<List<Entity>> ConvertToEntityAsync(IEnumerable<EventData> messages)
{
var serializedMessages = Serialize(messages);
var entities = autoMapper.Map<Entity[]>(serializedMessages);
entities = await DoAnotherWork(entities);
return entities;
}
Почему Task.Run () разрешает возникшую тупиковую ситуацию? (подробности см. ниже)
. NET Ядро вообще не имеет "контекста" , поэтому оно использует контекст пула потоков. Поскольку. NET Ядро не имеет контекста, оно удаляет второе условие тупика, и тупик не возникает. Если вы выполняете это в ASP. NET Базовом проекте.
Моя команда несколько раз сталкивалась с проблемами взаимоблокировки, когда мы использовали AbcAsyn c () .Result или .Wait () (метод вызывался в NET Базовых методах веб-API и взаимоблокировки возникали в основном при запуске модульных тестов, выполняющих метод)
Некоторые инфраструктуры модульных тестов do предоставляет контекст - прежде всего xUnit. Контекст, предоставляемый xUnit , является синхронизирующим контекстом, поэтому он действует скорее как контекст пользовательского интерфейса или ASP. NET pre-Core контекст. Поэтому, когда ваш код выполняется в модульном тесте, у него есть второе условие для взаимоблокировки, и может произойти взаимоблокировка.
Как отмечалось выше, лучшее решение состоит в том, чтобы удалить блокировка полностью; это будет иметь приятный побочный эффект от повышения эффективности вашего сервера. Но если блокировка должна быть выполнена, вам следует заключить код unit-test в Task.Run
, а не ASP. NET код ядра.