Как правило, да. ConfigureAwait (false) следует использовать для каждого ожидания, если только метод не нуждается в его контексте.
Я часто видел этот совет здесь, на Stack Overflow, и это даже то, что говорит Стивен Клири (Microsoft MVP) в его статье Asyn c и Await :
Хорошее практическое правило - использовать ConfigureAwait (false), если вы не знаете, что вам нужен контекст.
Стивен определенно знает свое дело, и я согласен с тем, что совет технически точен, но я всегда думал, что это плохой совет по двум причинам:
- новичкам и
- Риск обслуживания
Во-первых, это плохой совет для новичков, потому что контекст синхронизации - сложная тема. Если вы начинаете изучать async
/ await
с того, что вам говорят, что «ConfigureAwait(false)
следует использовать для каждого await
, если методу не нужен его контекст», но вы даже не знаете, что такое «контекст» и что он означает "нуждаться в нем", тогда вы не знаете, когда вы не должны его использовать, так что в итоге вы всегда используете его. Это означает, что вы можете столкнуться с ошибками, которые будет очень трудно выяснить, если вы не узнаете об этом. Да, вам действительно нужна была эта «контекстная» вещь, и эта волшебная вещь «ConfigureAwait» заставила вас ее потерять. Вы можете потратить часы, пытаясь понять это.
Для приложений любого типа, я считаю, совет действительно должен быть противоположным: не используйте ConfigureAwait
вообще, если вы не знаете, что он делает, и вы определили, что вам абсолютно не нужен контекст после этой строки.
Однако определение того, что вам не нужен контекст, может быть простым или довольно сложным в зависимости от того, какие методы вызываются после. Но даже тогда - и это вторая причина, по которой я не согласен с этим советом - то, что вам не нужен контекст после этой строки прямо сейчас , не означает, что какой-то код не будет добавлен позже, будет использовать контекст. Вам придется надеяться, что тот, кто вносит это изменение, знает, что делает ConfigureAwait(false)
, видит это и удаляет. Использование ConfigureAwait(false)
повсюду создает риск обслуживания.
Это то, что другой Стивен, Стивен Туб (сотрудник Microsoft), рекомендует в ConfigureAwait FAQ под заголовком «Когда следует использовать ConfigureAwait. (false)? ":
При написании приложений обычно требуется поведение по умолчанию (вот почему это поведение по умолчанию). ... Это приводит к общему руководству: если вы пишете код уровня приложения, не используйте ConfigureAwait(false)
В моем собственном коде приложения я не Не пытаюсь понять, где я могу и не могу это использовать. Я просто игнорирую существование ConfigureAwait
. Конечно, может повысить производительность, если использовать его там, где это возможно, но я действительно сомневаюсь, что это будет заметной разницей для любого человека, даже если это измерить таймером. Я не верю, что окупаемость инвестиций будет положительной.
Единственное исключение - когда вы пишете библиотеки, как указывает Стивен Туб в своей статье:
если вы пишете код библиотеки общего назначения, используйте ConfigureAwait(false)
Это по двум причинам:
- Библиотека ничего не знает о контекст приложения, в котором он используется, поэтому он все равно не может использовать контекст, и
- Если человек, использующий библиотеку, решит синхронно ждать кода вашей асинхронной библиотеки, это может вызвать тупик, который они не могут изменить, потому что они не могут изменить ваш код. (в идеале они не должны этого делать, но это может случиться)
Чтобы ответить на другой вопрос в вашем вопросе: не всегда достаточно использовать ConfigureAwait(false)
на первом await
, а не на остальное. Используйте его на каждые await
в коде вашей библиотеки. Статья Стивена Туба под заголовком «Можно ли использовать ConfigureAwait (false) только для первого ожидания в моем методе, а не для остальных?» частично говорит:
Если await task.ConfigureAwait(false)
включает в себя задачу, которая уже завершена к моменту ее ожидания (что на самом деле невероятно распространено), то ConfigureAwait(false)
будет бессмысленным, поскольку поток продолжает выполнять код в методе после этого и по-прежнему в том же контексте, что и раньше.