Когда правильно использовать задачу.Запустите и когда просто асинхронно-ждите
Я хотел бы спросить вас о вашем мнении о правильной архитектуре, когда использовать Task.Run. Я испытываю интерфейс лагает в нашем приложении WPF .Нетто 4.5
применение (с рамками Caliburn микро).
в основном я делаю (очень упрощенные фрагменты кода):
public class PageViewModel : IHandle<SomeMessage>
{
...
public async void Handle(SomeMessage message)
{
ShowLoadingAnimation();
// Makes UI very laggy, but still not dead
await this.contentLoader.LoadContentAsync();
HideLoadingAnimation();
}
}
public class ContentLoader
{
public async Task LoadContentAsync()
{
await DoCpuBoundWorkAsync();
await DoIoBoundWorkAsync();
await DoCpuBoundWorkAsync();
// I am not really sure what all I can consider as CPU bound as slowing down the UI
await DoSomeOtherWorkAsync();
}
}
из статей / видео, которые я читал / видел, я знаю, что awaitasync не обязательно работает на фоновом потоке и для начала работы в фоновом режиме вам нужно обернуть его с await Task.Run(async () => ... ). Используя asyncawait не блокирует пользовательский интерфейс, но все же он работает в потоке пользовательского интерфейса, поэтому он делает его лаговым.
где лучше всего поставить задачу.Бежать?
я должен просто
оберните внешний вызов, потому что это меньше потоковой работы для .NET
, или я должен обернуть только методы, связанные с процессором, внутренне работающие с
Task.Runкак это делает его многоразовым в других местах? Я не уверен, что здесь, если начать работа над фоновыми потоками глубоко в ядре-хорошая идея.
реклама (1), Первое решение будет выглядеть так:
public async void Handle(SomeMessage message)
{
ShowLoadingAnimation();
await Task.Run(async () => await this.contentLoader.LoadContentAsync());
HideLoadingAnimation();
}
// Other methods do not use Task.Run as everything regardless
// if I/O or CPU bound would now run in the background.
Реклама (2), второе решение будет выглядеть так:
public async Task DoCpuBoundWorkAsync()
{
await Task.Run(() => {
// Do lot of work here
});
}
public async Task DoSomeOtherWorkAsync(
{
// I am not sure how to handle this methods -
// probably need to test one by one, if it is slowing down UI
}
2 ответов:
Примечание рекомендации по выполнению работы с потоком пользовательского интерфейса, собранные в моем блоге:
- не блокируйте поток пользовательского интерфейса более чем на 50 мс одновременно.
- вы можете запланировать ~100 продолжений в потоке пользовательского интерфейса в секунду; 1000-это слишком много.
есть два метода, которые вы должны использовать:
1) Использовать
ConfigureAwait(false)когда вы можете.например,
await MyAsync().ConfigureAwait(false);вместоawait MyAsync();.
ConfigureAwait(false)рассказываетawaitчто вам не нужно возобновлять работу в текущем контексте (в этом случае "в текущем контексте" означает "в потоке пользовательского интерфейса"). Однако, для остального этогоasyncспособ (послеConfigureAwait), вы не можете сделать ничего, что предполагает, что вы находитесь в текущем контексте (например, обновить элементы пользовательского интерфейса).для получения дополнительной информации см. мою статью MSDN лучшие практики в асинхронное программирование.
2) Использовать
Task.Runдля вызова методов, связанных с ЦП.вы должны использовать
Task.Run, но не в любом коде, который вы хотите использовать повторно (т. е. код библиотеки). Так что вы используетеTask.Runдо вызов метод, а не как часть реализация метода.таким образом, чисто связанная с процессором работа будет выглядеть так:
// Documentation: This method is CPU-bound. void DoWork();который вы бы назвали с помощью
Task.Run:await Task.Run(() => DoWork());методы, которые являются смесь of CPU-bound и I / O-bound должны иметь
Asyncподпись с документацией, указывающей на их привязку к процессору:// Documentation: This method is CPU-bound. Task DoWorkAsync();который вы звонок с помощью
Task.Run(поскольку он частично связан с процессором):await Task.Run(() => DoWorkAsync());
одна проблема с вашим ContentLoader заключается в том, что внутренне он работает последовательно. Лучший шаблон-распараллелить работу, а затем синхронизировать в конце, поэтому мы получаем
public class PageViewModel : IHandle<SomeMessage> { ... public async void Handle(SomeMessage message) { ShowLoadingAnimation(); // makes UI very laggy, but still not dead await this.contentLoader.LoadContentAsync(); HideLoadingAnimation(); } } public class ContentLoader { public async Task LoadContentAsync() { var tasks = new List<Task>(); tasks.Add(DoCpuBoundWorkAsync()); tasks.Add(DoIoBoundWorkAsync()); tasks.Add(DoCpuBoundWorkAsync()); tasks.Add(DoSomeOtherWorkAsync()); await Task.WhenAll(tasks).ConfigureAwait(false); } }очевидно, что это не работает, если какая-либо из задач требует данных из других более ранних задач, но должно дать вам лучшую общую пропускную способность для большинства сценариев.
Comments