Следует ли использовать Task.Run в рамках другой задачи? - PullRequest
0 голосов
/ 08 сентября 2018

У меня есть куча работы (связанной с ЦП или IO без использования асинхронного интерфейса) внутри Задачи. Мне интересно, можно ли просто выполнять всю не асинхронную работу в рамках задачи, например:

async Task DoSomeStuff()
{
    await SomethingAsync();
    …
    DoCpuBoundWork();
    …
    await SomethingElseAsync();
}

или я должен использовать Task.Run, как это?

async Task DoSomeStuff()
{
    await SomethingAsync();
    …
    await Task.Run(() => DoCpuBoundWork());
    …
    await SomethingElseAsync();
}

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

Я уверен, что этот вопрос задавался ранее, но я не могу найти правильные ключевые слова для его поиска, так что извините за это.

Ответы [ 2 ]

0 голосов
/ 09 сентября 2018

Как отмечено в на этой странице (раздел Глубокое погружение в задачу и задачу для операции с привязкой к ЦП), задачи выполняются в потоке, из которого они вызваны, и работа с привязкой к ЦП действительно должен быть заключен в Task.Run, чтобы он работал в другом (фоновом) потоке. Так что да, нормально и нормально использовать Task.Run в асинхронном методе для работы с процессором или блокирования работы. Цитируется со страницы:

public async Task<int> CalculateResult(InputData data)
{
    // This queues up the work on the threadpool.
    var expensiveResultTask = Task.Run(() => DoExpensiveCalculation(data));

    // Note that at this point, you can do some other work concurrently,
    // as CalculateResult() is still executing!

    // Execution of CalculateResult is yielded here!
    var result = await expensiveResultTask;

    return result;
}

CalculateResult () выполняется в потоке, в котором он был вызван. Когда он вызывает Task.Run, он ставит в очередь дорогостоящую операцию с привязкой к процессору DoExорогоCalculation () в пуле потоков и получает дескриптор задачи. В конечном итоге DoExoyCalculation () запускается одновременно в следующем доступном потоке, вероятно, на другом ядре ЦП. Одновременно можно выполнять параллельную работу, когда DoExорогоCalculation () занята в другом потоке, поскольку поток, вызвавший CalculateResult (), все еще выполняется.

Как только ожидается ожидание, выполнение CalculateResult () передается вызывающей стороне, что позволяет выполнять другую работу с текущим потоком, пока DoExadvanCalculation () производит вывод результата. Как только он закончится, результат ставится в очередь для запуска в главном потоке. В конце концов основной поток вернется к выполнению CalculateResult (), после чего у него будет результат DoExoyCalculation ().

0 голосов
/ 08 сентября 2018

Ответ: это зависит.

Вы указали на планировщик, вот в чем проблема. Если вы работаете в пуле потоков (планировщик по умолчанию), тогда синхронная работа с привязкой к процессору вполне подойдет. Если вы работаете в потоке пользовательского интерфейса, это может привести к ухудшению взаимодействия с пользователем.

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

В двух словах, не используйте await Task.Run(() => DoCpuBoundWork());. Пусть звонящий, который знает контекст исполнения, решит за вас.

...