Могу ли я получить контекст потока пользовательского интерфейса при использовании 'async / await' - PullRequest
0 голосов
/ 18 января 2019

Я хочу знать, возможно ли получить контекст потока пользовательского интерфейса при использовании метода Asyn / await. Если да, то как?

Код, написанный с использованием шаблона WPF / MVVM:

long asyncTimeChecker = 0;
int prevSec2 = 0;

/// <summary>
/// Button Click Command
/// </summary>
private void ExeTimerActivate(object o)
{
   if (!IsTimerStart)
   {
      ActivateAsyncTicToc();
      TimerState = "Stop";
   }
   else if (IsTimerStart)
   {
      ActivateAsyncTicToc();
      TimerState = "Start";
   }

   IsTimerStart = !IsTimerStart;
}        

/// <summary>
/// Call Method By Async/Await
/// </summary>
private async void ActivateAsyncTicToc()
{
  IsTimerStartAsync = !IsTimerStartAsync;

  var task1 = Task.Run(() => AsyncTicToc());
  await task1;
}



/// <summary>
/// I tried to UI access by other thread what use Async/Await
/// </summary>
private void AsyncTicToc()
{
  while (IsTimerStartAsync)
  {
      System.Threading.Thread.Sleep(10);
      AsyncTimeText = $"{asyncTimeChecker / 1000}.{asyncTimeChecker % 1000}";
      asyncTimeChecker += 10;

      /// ========================================
      /// This Position Get CrossThread Problem
      /// ========================================
      if (prevSec2 < asyncTimeChecker / 1000)
      {
           prevSec2++;
           if (TimerColor2.Color == Colors.Blue)
               TimerColor2.Color = Colors.Red;
           else
               TimerColor2.Color = Colors.Blue;
       }
   }

}

Я знаю, что мы можем получить поток пользовательского интерфейса, используя Dispatcher, но хочу знать, возможно ли это с помощью async / await.

Ответы [ 3 ]

0 голосов
/ 18 января 2019

Прежде всего, вы можете использовать await Task.Delay(10) вместо Thread.Sleep(10) и изменить свой AsyncTicToc метод на async Task без запуска потока. Это сделает изменение элемента управления в потоке пользовательского интерфейса (контекст, от которого вы ожидали).

Во-вторых, чтобы обновить пользовательский интерфейс из других потоков, если вы не хотите использовать диспетчер напрямую, вы можете использовать абстракцию над ним: класс SynchronizationContext . Подробнее здесь .

Пример: SynchronizationContext.Current.Post(action, actionParameter);

В-третьих, более целесообразно использовать модель представления и привязку. Если вы используете WPF, он синхронизирует потоки для изменений свойств для вас.

0 голосов
/ 27 января 2019

Несмотря на то, что есть возможность «выйти» из другого потока в поток пользовательского интерфейса, используя Dispatcher или SynchronizationContext, я настоятельно рекомендую этого не делать. Это потому, что это делает вашу логику менее тестируемой и более привязанной к ее среде. В случае Dispatcher он сильно привязан к работе в среде WPF; SynchronizationContext лучше, но он все еще привязан к работе в какой-то среде.

При таком подходе ваша логика зависит от потока пользовательского интерфейса, например:

код пользовательского интерфейса => логика фонового потока => поток пользовательского интерфейса

Вместо этого используйте IProgress<T> и Progress<T> из фоновой логики потока, чтобы сообщать отчеты о ходе работы своему вызывающему, который решает, как отображать эти отчеты о ходе работы. Тогда ваша зависимость выглядит так:

Код интерфейса => логика фонового потока

и ваша фоновая логика потока не зависит от наличия потока пользовательского интерфейса. Это делает его более удобным и проверяемым.

0 голосов
/ 18 января 2019

Вы завершили свой вызов async в задании. Это побеждает всю машину состояний и заканчивается ненужным использованием потока потоков.

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

Как минимум, вы должны делать это, если вам абсолютно необходимо использовать async void, он просто ожидает задания, а не упаковывает его

private async void ActivateAsyncTicToc()
{
   try
   {
      IsTimerStartAsync = !IsTimerStartAsync;    
      await AsyncTicToc();
   }
   catch (Exception e)
   {
      // make sure you observe exceptions in async void
   }
}

Еще лучше, пусть async распространяется с использованием async Task

private async Task ActivateAsyncTicToc()
{
   IsTimerStartAsync = !IsTimerStartAsync;    
   await AsyncTicToc();
}

Что в любом случае выглядит подозрительно, вы, вероятно, должны делать это (Eliding async и await) и просто передавать задание кому-то еще, кто будет ждать. это небольшой прирост производительности

private Task ActivateAsyncTicToc()
{
   IsTimerStartAsync = !IsTimerStartAsync;    
   return AsyncTicToc();
}

На самом деле это минное поле, и я буду здесь весь день

Вам нужно начать читать о async и await

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...