как я могу распознать истинно асинхронные / синхронизирующие методы?
Ты не можешь. На самом деле, нет. Вы можете определить методы, которые потенциально являются асинхронными, но мало что можно узнать без обращения к документации или реализации этих методов.
Итак, вы можете проверить тип возвращаемого значения метода. Если это void
, вы мало что знаете. Если это Task
, Task<T>
, ValueTask<T>
или любого другого ожидаемого типа 1 , то метод может быть асинхронным. Но имейте в виду, сигнатура метода может быть исправлена, потому что тип унаследовал метод от базового класса или реализует интерфейс; Поэтому, хотя метод потенциально может быть асинхронным, фактическая реализация этого метода может быть полностью синхронной.
Или, метод может иметь потенциал быть асинхронным, но может иметь определенные потоки управления, которые приводят к тому, что он ведет себя синхронно 2 . Это могут быть, например, что если определенные условия выполняются, у метода уже есть ответ, поэтому он сразу его возвращает. В противном случае он отключается и выполняет что-то асинхронное - как один из примеров.
Если возвращаемый тип не является ожидаемым и не является пустым, все, что вы можете на самом деле рассуждать о методе, это то, что в точке, в которой он возвращается, ему предоставляется некоторое значение для этого возвращаемого типа 3 . Нет никакого способа рассуждать о какой-либо другой работе , которая могла быть запущена этим методом - только то, что если она сделала что-то подобное, она не предполагает, что вы сможете узнать, когда эта работа будет завершена .
Если вы смотрите на реализацию метода и спрашиваете себя «является ли эта реализация асинхронной», тогда важно выяснить, что делает этот код после того, как элемент управления возвращается обратно вызывающей стороне. ,
Когда управление возвращается обратно вызывающей стороне?
- Когда мы нажмем
return
- Когда мы нажимаем
await
, возможно
Когда мы нажимаем await
, мы возвращаем управление обратно вызывающей стороне 4 , если ожидание того, что мы await
не завершены. Таким образом, мы должны выяснить, откуда появилось это ожидаемое, и, если это произошло из-за вызова другого метода, мы должны начать все заново, чтобы рассмотреть, что делает этот метод.
Если метод содержит await
с, то, как правило, безопаснее сказать, что он потенциально асинхронный - но имейте в виду вышеупомянутые возможности относительно уже завершенных ожидаемых и ранних return
с.
Если это не async
/ await
, что еще он мог бы сделать? Что ж, если он работает с Task
s, возможно, он создал одну или несколько из этих задач, чтобы представить текущую работу. Возможно, было запланировано больше кода для запуска через ContinueWith
.
Если он не работает с Task
напрямую, не вызвал что-то еще, что потенциально асинхронно, не вызвало создание нового Thread
и не было бесполезно запутано, это , вероятно синхронно.
Можно ли использовать это в ситуациях, когда, например, у меня очень простой код и я хочу избежать проблем с многопоточностью / взаимоблокировкой, так как я пока не знаком с асинхронным программированием?
Синхронизация по асинхронным шаблонам, показанным в примерах в вашем вопросе, на больше подвержена взаимным блокировкам, чем работа с async
/ await
. Зачем? Потому что они блокируют текущий поток , ожидая, когда произойдут другие события. Но текущий поток или ресурсы, которые он заблокировал, могут быть особыми - и фактическим асинхронным задачам, которые он вызывает, может потребоваться получить доступ к тому же потоку / ресурсу, прежде чем они смогут завершиться. Классический тупик.
1 Awaitable - одно из многих мест, где C # использует "типизацию утки". Если есть метод (экземпляр или расширение) с именем GetAwaiter
, который возвращает тип с правильной формой, это приемлемо. Поэтому, несмотря на то, что вы, вероятно, никогда не увидите его, имейте в виду, что пользовательские ожидаемые значения возможны в языке.
2 На ум приходят "горячие пути".
3 И out
/ ref
Параметры. Конечно, если он есть, это не будет асинхронный метод, реализованный через ключевые слова async
/ await
, но он все равно может иметь асинхронное поведение.
4 Если мы уже передали управление обратно вызывающей стороне во время более раннего await
, мы не вернем управление обратно вызывающей стороне через await
с, но мы будем возвращая это «чему-то», которое не является нашим кодом.