繁体   English   中英

使用“async/await”时可以获取 UI 线程上下文吗

[英]Can I get UI thread context while using 'async/await'

我想知道在使用 Asyn/await 方法时是否可以获取 UI 线程上下文。 如果是,如何?

使用 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 获取 UI 线程,但想知道是否可以使用 async/await。

您将async调用包装在一个任务中。 这会破坏整个状态机,并最终不必要地使用线程池线程。

调用和任何延续都会在另一个线程中结束,因此如果不进行编组就无法访问 UI。

如果您绝对需要使用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();
}

无论如何,这看起来很可疑,您可能应该这样做(省略asyncawait )并将任务传递给将要等待的其他人。 这是一个小的性能提升:

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

事实上,这是一个雷区,我会在这里待一整天。

您需要开始阅读有关asyncawait

首先,您可以使用await Task.Delay(10)而不是Thread.Sleep(10)并将您的AsyncTicToc方法更改为async Task而不启动线程。 这将更改 UI 线程(您等待的上下文)中的控件。

其次,要从其他线程更新 UI,如果不想直接使用调度程序,可以使用它的抽象: SynchronizationContext类。 在此处查看更多信息。

示例: SynchronizationContext.Current.Post(action, actionParameter);

第三,使用视图模型和绑定更合适。 如果您使用 WPF,它会为您同步线程以进行属性更改。

虽然可以使用DispatcherSynchronizationContext从另一个线程“伸出”到 UI 线程,但我强烈建议不要这样做。 这是因为它使您的逻辑更难测试并且更受其环境约束。 Dispatcher的情况下,它与在 WPF 环境中运行紧密相关; SynchronizationContext更好,但它仍然依赖于在某种环境中运行。

使用这种方法,您的逻辑与 UI 线程之间存在依赖关系,如下所示:

UI 代码 => 后台线程逻辑 => UI 线程

相反,使用后台线程逻辑中的IProgress<T>Progress<T>将进度报告传送回其调用方,后者决定如何显示这些进度报告。 然后你的依赖看起来像这样:

UI 代码 => 后台线程逻辑

并且您的后台线程逻辑不依赖于存在的 UI 线程。 这使其更具可重用性和可测试性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM