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

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

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?
}

1 ответ

Чтобы ответить на заглавный вопрос "Когда используется ConfigureAwait(false) в ViewModels проблематично?" is "Never.". Его использование в модели представления не имеет значения. Важно то, в каком потоке вы хотите запускаться после вызова асинхронного метода, независимо от того, находитесь ли вы в модели представления, не имеет значения. время, которое с помощью 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 использования Task.Run или же Thread.StartNew которые являются основными способами запуска новой темы... await не запускает новый поток, это всего лишь синтаксический сахар, позволяющий сделать ваш асинхронный код более читабельным, так что код, следующий за вызовом асинхронного метода, не обязательно должен быть методом делегата или лямбда-выражением, а может быть встроенным, как синхронный код.)

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

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

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

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

Другие вопросы по тегам