Когда проблематично использование ConfigureAwait (false) в ViewModels?

64
9

Интересно, в каких ситуациях у меня возникнут проблемы при использовании

ConfigureAwait(false)

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

1 А как насчет наблюдаемых коллекций?

// VM
public ObservableCollection<SomeThing> SomeThings { get; set; }
// ...
public Task InitWorkload()
{
SomeThings = await DbService.GetSomeThings(); // <-- Should need synchronization context, doesn't it?
}

// Service
public Task<SomeThings> GetSomeThings()
{
result = await CallToDb.ConfigureAwait(false); // <-- This is UI agnostic and shouldn't care about context or does it?
return result;
}

2 А как насчет навигации (в данном случае с помощью FreshMvvm)?

private async Task CloseWindow()
{
await CoreMethods.PopPageModel(); // <-- Should need synchronization context, doesn't it?
}

спросил(а) 2021-01-19T14:53:43+03:00 6 месяцев, 1 неделя назад
1
Решение
90

Чтобы ответить на заглавный вопрос: "Когда проблематично использование ConfigureAwait(false) в ViewModels?" никогда." Его использование в модели представления не имеет значения. Важно то, в каком потоке вы хотите запускаться после вызова асинхронного метода, независимо от того, находитесь ли вы в модели представления или нет. Единственное время, когда использование ConfigureAwait(false) может быть проблематичным, это если вы хотите вернуться к потоку, который был запущен до вызова асинхронного метода.

И для справки, документы по классу SynchronizationContext.

Возможно, объяснение того, что делает ConfigureAwait(false), является лучшим подходом для ответа на этот вопрос. Когда кто-то вызывает асинхронный метод, например, так:

var x = await SomeMethodAsync();
var y = x;

Код var y = x будет выполняться в том же потоке, над которым выполнялась работа до вызова асинхронного метода, то есть в контексте синхронизации до вызова асинхронного метода, однако, если вы используете ConfigureAwait (false), например:

var x = await SomeMethodAsync().ConfigureAwait(false);
var y = x;

тогда код var y = x будет выполняться в том же потоке, в SomeMethodAsync был запущен метод SomeMethodAsync когда он вернулся. (Предположительно SomeMethodAsync использует Task.Run или Thread.StartNew которые являются основными способами запуска нового потока... await не запускает новый поток, это всего лишь синтаксический сахар, позволяющий сделать ваш асинхронный код более читабельным, чтобы код после вызова асинхронного метода не обязательно должен быть метод делегата или лямбда, но он может быть встроенным, как синхронный код.)

То, что вам нужно, зависит от того, что может потребоваться обновить. Любые обновления пользовательского интерфейса должны выполняться в основном потоке или пользовательском интерфейсе. Вы всегда можете Device.BeginInvokeOnMainThread(Action) код в основной поток или поток пользовательского интерфейса с помощью Device.BeginInvokeOnMainThread(Action).

Обычно, когда вы находитесь, скажем, в событии обработчика нажатия кнопки, метод обработчика событий запускается в потоке пользовательского интерфейса. Если вам нужно вызвать один асинхронный метод, а затем обновить какой-либо пользовательский интерфейс, не используйте ConfigureAwait(false) чтобы после асинхронного метода вы вернулись в поток Main/UI и могли обновить свой пользовательский интерфейс. Если вам не нужно обновлять какой-либо пользовательский интерфейс, не стесняйтесь вызывать ConfigureAwait(false) чтобы вы не возвращались без необходимости в поток пользовательского интерфейса, когда нет необходимости работать в этом потоке.

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

Что касается ObservableCollection, он не имеет привязки к потоку, т.е. Когда объект может быть обновлен или изменен только в том же потоке, в котором он был создан, но он также не является потокобезопасным, поэтому ваш лучший план - только доступ или изменение. ObservableCollection в том же потоке, в котором он был создан, скорее всего, в главном/пользовательском интерфейсе.

ответил(а) 2021-01-19T14:53:43+03:00 6 месяцев, 1 неделя назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема