Вы задали несколько вопросов в своем вопросе. Я сломаю их немного иначе, чем ты. Но сначала позвольте мне прямо ответить на вопрос.
Мы все хотим, чтобы камера была легкой, качественной и дешевой, но, как говорится, вы можете получить только два из этих трех. Вы находитесь в такой же ситуации здесь. Вы хотите эффективное решение, безопасное и разделяющее код между синхронными и асинхронными путями. Вы получите только два из них.
Позвольте мне объяснить, почему это так. Начнем с этого вопроса:
Я видел, что люди говорят, что вы можете использовать GetAwaiter().GetResult()
асинхронный метод и вызывать его из вашего метода синхронизации? Этот поток безопасен во всех сценариях?
Суть этого вопроса заключается в том, «могу ли я разделить синхронные и асинхронные пути, заставив синхронный путь просто выполнить синхронное ожидание в асинхронной версии?»
Позвольте мне быть предельно ясным по этому вопросу, потому что это важно:
ВЫ ДОЛЖНЫ НЕМЕДЛЕННО ОСТАНОВИТЬСЯ, ЧТОБЫ СОВЕТАТЬСЯ С ЭТИМИ ЛЮДЯМИ .
Это очень плохой совет. Очень опасно синхронно получать результат от асинхронной задачи, если у вас нет доказательств того, что задача выполнена нормально или ненормально .
Причина, по которой это крайне плохой совет, заключается в том, чтобы рассмотреть этот сценарий. Вы хотите косить газон, но лезвие вашей газонокосилки сломано. Вы решили следовать этому рабочему процессу:
- Закажите новый клинок с веб-сайта. Это асинхронная операция с высокой задержкой.
- Синхронно ждите, то есть спите, пока у вас не будет лезвия в руке .
- Периодически проверяйте почтовый ящик, чтобы увидеть, прибыл ли блейд.
- Выньте лезвие из коробки. Теперь у вас есть это в руках.
- Установите лезвие в косилку.
- Косить газон.
Что просходит? Вы спите вечно, потому что операция проверки почты теперь зависит от того, что происходит после получения почты .
Очень легко попасть в такую ситуацию, когда вы синхронно ожидаете произвольную задачу. У этой задачи может быть запланирована работа в будущем потока, который сейчас ожидает , и теперь это будущее никогда не наступит, потому что вы его ожидаете.
Если вы делаете асинхронное ожидание, тогда все в порядке! Вы периодически проверяете почту, и пока вы ждете, вы делаете бутерброд или платите налоги или что-то еще; Вы продолжаете работать, пока ждете.
Никогда не ждите синхронно. Если задача выполнена, это не нужно . Если задача не выполнена, но запланирована для запуска из текущего потока, она неэффективна, поскольку текущий поток может обслуживать другую работу вместо ожидания. Если задача не выполнена и расписание запускается в текущем потоке, она зависает, чтобы синхронно ждать. Нет никакой веской причины для синхронного ожидания, если только вы не знаете, что задача выполнена .
Для дальнейшего чтения по этой теме, см.
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Стивен объясняет сценарий реального мира гораздо лучше, чем я.
Теперь давайте рассмотрим «другое направление». Можем ли мы поделиться кодом, сделав асинхронную версию просто выполняющей синхронную версию в рабочем потоке?
То есть , возможно , и в самом деле , вероятно , плохая идея, по следующим причинам.
Это неэффективно, если синхронная операция ввода-вывода с высокой задержкой. По сути, это нанимает работника и заставляет его спать до тех пор, пока задача не будет выполнена. Нитки безумно дороги . По умолчанию они потребляют минимум миллион байтов адресного пространства, им требуется время, они берут ресурсы операционной системы; Вы не хотите прожечь нить, делая бесполезную работу.
Синхронная операция может быть записана не как потокобезопасная.
Это является более разумным , если метод высокой латентностью работы процессора связаны, но если это , то вы , вероятно , не хотите , чтобы просто передать его в рабочий поток. Вы, вероятно, захотите использовать библиотеку параллельных задач, чтобы распараллелить ее как можно большему количеству процессоров, вы, вероятно, захотите логику отмены, и вы не можете просто заставить синхронную версию делать все это, потому что тогда это будет уже асинхронная версия .
Дальнейшее чтение; опять же, Стивен объясняет это очень четко:
Почему бы не использовать Task.Run:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html
Больше сценариев «делай и не делай» для Task.Run:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
Что это тогда оставляет нас с? Обе технологии для совместного использования кода приводят либо к тупикам, либо к большой неэффективности. Мы пришли к выводу, что вы должны сделать выбор. Вам нужна программа, которая является эффективной и правильной и доставляет удовольствие вызывающей стороне, или вы хотите сохранить несколько нажатий клавиш, дублируя небольшой объем кода между синхронным и асинхронным путями? Боюсь, вы не получите и то, и другое.