Когда правильно использовать задачу.Запустите и когда просто асинхронно-ждите



Я хотел бы спросить вас о вашем мнении о правильной архитектуре, когда использовать 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 не блокирует пользовательский интерфейс, но все же он работает в потоке пользовательского интерфейса, поэтому он делает его лаговым.



где лучше всего поставить задачу.Бежать?



я должен просто




  1. оберните внешний вызов, потому что это меньше потоковой работы для .NET


  2. , или я должен обернуть только методы, связанные с процессором, внутренне работающие с 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
}
627   2  

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

    Ничего не найдено.