Что на самом деле делает доступ к Result для задачи перед вызовом Wait? - PullRequest
0 голосов
/ 05 февраля 2020

var task = Task.Run(() => DoSomeStuff()).Result;

Что здесь происходит под капотом?

Я сделал крошечный тест:

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {
        var r = Task.Run( () => {Thread.Sleep(5000); return 123; }).Result;
        Console.WriteLine(r);
    }
}

Он печатает «123» через 5 с. Так что доступ к любому такому свойству на Task действует как ярлык для вызова Task.Wait(), т. Е. Безопасно ли это делать?


Ранее мой код вызывал Task.Delay(5000), который немедленно возвратил "123". Я исправил это в своем вопросе, но оставил это здесь, поскольку комментарии и ответы ссылаются на него.

Ответы [ 3 ]

4 голосов
/ 05 февраля 2020

Вы задали два вопроса. Во-первых, вызывает ли доступ к Result неявно синхронный Wait? Да. Но более важный вопрос:

это безопасно делать?

Это небезопасно.

Очень легко попасть в ситуацию, когда задача, которую вы ожидаете синхронно, имеет запланированную работу , выполняемую в будущем до завершения в потоке , который вы только что перевели в режим ожидания . Теперь у нас есть ситуация, когда поток не проснется, пока поток sleep не выполнит какую-то работу, которую он никогда не сделает , потому что он спит .

Если вы уже знайте, что задача выполнена, тогда можно безопасно ожидать результата синхронно. Если вы этого не сделаете, тогда небезопасно синхронно ждать.

Теперь, вы можете сказать, предположим, что я знаю с помощью других средств, что безопасно синхронно ждать незавершенного задания , Тогда это безопасно ждать? Ну, , исходя из вопроса , да, но все равно может быть умным ждать. Помните, весь смысл асинхронности заключается в эффективном управлении ресурсами в мире с высокой задержкой . Если вы синхронно ожидаете завершения асинхронной задачи, вы заставляете работника спать, пока другой работник не завершит работу; спящий работник может делать работу! Весь смысл асинхронности в том, чтобы избегать ситуаций, когда рабочие go простаивают, поэтому не заставляйте их.

await - это асинхронное ожидание, Это ожидание означает, что «дождитесь запуска остальной части текущего рабочего процесса до тех пор, пока эта задача не будет выполнена, но найдите что-то делать, пока вы ждете». Мы проделали большую работу, чтобы добавить его к языку, поэтому используйте его!

2 голосов
/ 05 февраля 2020

Тест не ждет Task.Delay(), поэтому он немедленно возвращается. Должно быть:

var r = Task.Run(async  () => { await Task.Delay(5000); return 123; }).Result;

Поведение Result четко определено - если задание не выполнено, оно блокируется, пока не выполнит. Доступ к другим свойствам задачи не блокирует

2 голосов
/ 05 февраля 2020

Так что доступ к любому такому свойству на Task действует как ярлык для вызова Task.Wait()?

Да.

Из документов :

Доступ к свойству get доступа [Result] блокирует вызывающий поток до завершения асинхронной операции; это эквивалентно вызову Wait метода.

Однако ваш тест не выполняет то, о чем вы думаете.

Task.Delay(..) возвращает Task, что завершается через указанное количество времени. Он не блокирует вызывающий поток.

Так что () => { Task.Delay(5000); return 123; } просто создает новый Task (который завершится через 5 секунд), затем выбрасывает его и немедленно возвращает 123.

Вы можете либо:

  1. заблокировать вызывающий поток, выполнив Task.Delay(5000).Wait() (что делает то же самое, что и Thread.Sleep(5000))
  2. Асинхронно дождитесь возврата Task от Task.Delay до завершения: Task.Run(async () => { await Task.Delay(5000); return 123; })
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...