Мне трудно понять преимущества переноса асинхронной работы на Task.Run(). Раньше было много вопросов об асинхронной работе, выгружаемой в задачу, но я никогда не видел, чтобы решалась самая большая загадка для меня.
рассмотреть два варианта:
var t1= Task.Run( async() => { await CPUBoundWorkAsync(); });
var t2= Task.Run( () => { CPUBoundWork(); });
При втором варианте работа выгружается в отдельный поток, таким образом «достигается» асинхронность, и основной поток может вернуться к тому, для чего он нужен. При первом варианте используется асинхронный метод, а делегат выражается как асинхронный. Оба варианта должны давать неотличимый результат с точки зрения отсутствия блокировки основного потока и одновременного завершения работы, связанной с процессором. Есть ли польза от использования асинхронного метода в Task.Run? мне кажется, что это по сути, позволяя работе, которая уже выполняется асинхронно (в другом неблокирующем потоке), также выполняться асинхронно.. Может ли пул потоков или что-то еще, управляющее этими потоками задач, прочитать это? а затем, возможно, дать этому ожидающему потоку какую-то другую работу? если это так, я не видел упоминания об этом ни в одном блоге эксперта или на соответствующей странице в Интернете.
@LasseV.Karlsen, вы правы, отредактировал вопрос.
Ваш вопрос связан с этим? ожидание Task.Run против ожидания
@TheodorZoulias, этот вопрос касается чего-то другого и гораздо более простого
Отвечает ли это на ваш вопрос? ожидание Task.Run против ожидания
@MKR отвечает на другой вопрос
Честно говоря, я не понимаю вопроса. Вы представляете два варианта, которые имеют значение только в меньшинстве случаев, когда синхронный и асинхронный API доступны для одной и той же функциональности (Read
/AsyncRead
в вашем примере). Но вы не спрашиваете, какой из них лучше, потому что я предполагаю, что вы это уже знаете. Таким образом, вы на самом деле сравниваете не вариант 1 с вариантом 2, а вариант 1 с вариантом 3, не говоря, что такое вариант 3, и подразумевая, что это не естественно var t3 = AsyncRead();
. Хотите уточнить?
@TheodorZoulias Стивен Клири понял и отлично ответил на свой ответ ниже. Он указал, что метод возвращается в пул потоков после «ожидания», поэтому поток возвращается и может выполнять другую работу, что является «выгодой» от этого в контексте Task.Run().
@ Бен Стивен Клири, возможно, лучше умеет читать мысли, чем остальные, и понял, что вы действительно хотели знать, из той небольшой информации, которую вы дали в вопросе. Итак, если вы намеревались получить ответ от Стивена, цель достигнута! :-)
@TheodorZoulias, или, возможно, вы мало склонны интерпретировать вопросы таким образом, чтобы это соответствовало вашей зоне комфорта знаний;)
Я не могу сказать, находится ли вопрос внутри или вне моей зоны комфорта знаний, потому что я не понимаю вопроса. Когда кто-то представляет два варианта, обычно он хочет сравнить эти два варианта. Это не то, что вы хотите, и поэтому я в замешательстве.
@TheodorZoulias Я думаю, что это довольно ясно, и я снова пересмотрел его, чтобы сделать его еще яснее, очевидно, ответ, предоставленный Стивеном, дал на него точный и короткий ответ. вопрос затрагивает важную, но нюансированную деталь, и что простой ответ на него отсутствует во многих источниках, которые я проверил, включая сообщения в блогах экспертов и онлайн-курсы обучения. Я чувствую, что этот вопрос может помочь еще нескольким разработчикам, которые могут наткнуться на асинхронность в контексте Task.Run и быть пронизанными таким же образом.
Мне до сих пор непонятно, потому что я не знаю, что вы хотите с чем сравнить. Если вопрос "зачем мне использовать X, если нет другого выхода", то очевидный ответ — «вы вынуждены использовать Х, потому что другого выхода нет». Пожалуйста, рассмотрите возможность представления осмысленных вариантов, чтобы вопрос был осмысленным.
Кстати, если вы думаете, что, изменив имена методов в вопросе с AsyncRead
/Read
на CPUBoundWorkAsync
/CPUBoundWork
, вопрос станет более ясным, по крайней мере, для меня это не так. Название CPUBoundWorkAsync
специально создает для меня противоречивые ожидания относительно того, что этот метод может делать внутри. CPUBound
-часть означает, что он интенсивно использует по крайней мере один поток, а Async
-часть — что он использует нет нити. Мне интересно, что другие люди думают об этом.
@TheodorZoulias, очевидно, я сделал больше, чем просто изменил имена, но делайте, как хотите.
В вашем примере, если f1()
и f2()
являются асинхронными (в том смысле, что они возвращают задачу), тогда ключевое слово async позволит вам ожидать тех, кто внутри вашего Task.Run()
.
Ваш вопрос похож на «почему вы помечаете метод Любые как асинхронный»… потому что вы хотите запускать асинхронный код внутри. Или, точнее, вы хотите, чтобы компилятор построил конечный автомат для обработки «ожидания» завершения асинхронной операции.
Я отредактировал вопрос, очевидно, я не имел в виду, что f1() и f2() возвращаются немедленно
Я понял, что только сейчас... По сути, это одно и то же... если вы хотите вызывать асинхронные методы внутри, используйте асинхронные, если нет, то не делайте этого код внутри лямбды до... преимущества такие же, как если бы любой код был определен как асинхронный и позволил компилятору выполнять тяжелую работу по состоянию этой задачи
Я не уверен, правильно ли я понял ваш вопрос. Ориентируясь на «по сути, это позволяет коду, который уже выполняется асинхронно, также выполняться асинхронно»:
Хотя запуск асинхронного кода внутри Task.Run
кажется бессмысленным, бывают ситуации, когда это может быть необходимо. Асинхронные методы всегда выполняются синхронно, пока не произойдет первый «настоящий» асинхронный вызов. Хорошим примером является использование фоновые услуги.
With the 2nd option, the work is already being offloaded to a separate thread, then the asynchronicity is already gained in the scope of the program that invoked t2.
Task.Run
не делает что-то асинхронным. Это позволяет вам запускать синхронный код в потоке пула потоков (и, таким образом, блокировать поток пула потоков вместо какого-либо другого потока), но я бы не сказал, что это «делает его асинхронным». Код по-прежнему синхронно блокирует поток.
Now although the work that is being done in the 1st option is declared and implemented as async, it's essentially letting code that is already being ran asynchronously, to also run itself asynchronously. What benefits from it ? Can the the scheduler or whatever manages those task threads read into this? and then perhaps give that awaiting thread some work on another task ?
Тредов нет await
. Способы await
. Когда поток запускает метод и попадает в await
, этот метод приостанавливается, метод возвращается к вызывающей стороне, а поток продолжает выполнение. В случае делегата, переданного Task.Run
, await
вызовет возврат метода -- в пул потоков, тем самым возвращая поток в пул потоков. Когда задача await
ed завершится, поток из пула потоков будет использоваться для возобновления выполнения метода; это может быть тот же поток или совершенно другой поток.
Дополнительная информация о как именно await
работает находится в моем блоге.
if this is the case I haven't seen it mentioned in any expert blog post, or relevant page on the internet.
У меня серия на Task.Run
этикет. По сути, Task.Run
в основном используется для разгрузки синхронной или связанной с процессором работы из потока пользовательского интерфейса в приложениях с графическим интерфейсом. Это также иногда полезно в сценариях на стороне сервера, если вы хотите быстро начать что-то обрабатывать, вернуться назад и взять следующее для обработки. Есть еще несколько вариантов использования, но они редки.
Все эти причины могут относиться к асинхронным API. Иногда API появляться асинхронны, но на самом деле это не так. Или некоторые методы являются асинхронными, но они выполняют нетривиальный объем синхронной работы первый, поэтому в некоторых случаях Task.Run
по-прежнему желателен даже с асинхронным кодом.
Привет спасибо. Подробная информация о потоке, возвращающемся в пул потоков, отвечает на вопрос. У меня было ощущение, что это должно быть поведение, но нигде не было указано явно, что я смотрел, возможно, это слишком много нюансов или очевидно для большинства программистов (?)
Являются ли
f1
иf2
неизменными между двумя вариантами? Это означает, что в первом варианте вы ожидаетеf1
до, вызываяf2
, тогда как во втором варианте вы вызываетеf1
, игнорируете возвращаемый объектTask
, не (a) ждете его завершения, а затем немедленно вызываетеf2
, возможно, покаf1
все еще выполняется ?