«asyn c Task» vs «return Task.Run» - PullRequest
       25

«asyn c Task» vs «return Task.Run»

0 голосов
/ 17 июня 2020

Я новичок в модели async / await c# и пытаюсь понять, являются ли эти два параметра по сути одним и тем же:

public Task LongRunningMethod()
{
    return Task.Run(async () =>
    {
        await DoStuff();
    });
}

//then call it
await LongRunningMethod();

и этот

public async Task LongRunningMethod()
{
    await DoStuff();
}

//then call it
await LongRunningMethod();

I Я думаю, что 1-й способ будет использовать дополнительный поток из пула ... И он также превращает задачу в дополнительную задачу. Или я не прав?

Ответы [ 3 ]

6 голосов
/ 17 июня 2020

Task.Run поставит своего делегата в очередь в пул потоков. Это вызывает две важные вещи:

  1. Любой код в DoStuff до первого асинхронного await будет выполняться в потоке пула потоков, а не в в вызывающем потоке .
  2. Код в DoStuff будет выполняться в контексте пула потоков вместо с использованием любого контекста, который был текущим из вызывающего потока .

Большинство В случаях выполнение асинхронной работы в Task.Run является ошибкой. Но иногда это полезно, например, если DoStuff выполняет тяжелую вычислительную работу перед тем, как действовать асинхронно, тогда Task.Run можно использовать для перемещения этой работы из потока пользовательского интерфейса.

2 голосов
/ 17 июня 2020

Они очень похожи на , но есть шанс, что DoStuff может завершиться синхронно; если это произойдет, ваш второй (только await) метод будет блокироваться на сколько угодно времени, в то время как первый (Task.Run) всегда будет возвращать неполный вызывающему (раскручивание их стека и планирование продолжения) и блокирующая работа выполняется в потоке пула.

Оба варианта могут быть желательными в разных сценариях ios!

Однако есть третий вариант, который выражает точно намерение «запустить остальное в другом месте» до тех пор, пока у вас нет SyncContext:

public async Task LongRunningMethod()
{
    await Task.Yield();
    await DoStuff(); // possibly with .ConfigureAwait(false)
}

Если нет SyncContext, это функционально аналогично версии Task.Run (что означает: она всегда будет возвращаться вызывающей стороне как неполная и запускать DoStuff, начиная с пула), только: без фактических Task.Run бит. Однако, если есть , это a SyncContext: go вернется в тот же контекст, что ... неудобно, и, к сожалению, ConfigureAwait(false) для YieldAwaitable, поэтому в этом случае вам нужно будет использовать Task.Run или аналогичный.

1 голос
/ 17 июня 2020

Название метода передает причину, по которой это делается так: «LongRunning». Вы можете передать флаг создания LongRunning в Task.Run() (чего нет в этом примере), и в качестве чистого потока реализации среда выполнения выделит для этого отдельный поток.

Даже ваш второй пример не совершенно правильно, в зависимости от обстоятельств. Если это библиотека, то действительно должно быть await DoStuff().ConfigureAwait(false);.

...