![](/img/trans.png)
[英]Releases UI thread while long method is running using await and async
[英]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();
}
无论如何,这看起来很可疑,您可能应该这样做(省略async
和await
)并将任务传递给将要等待的其他人。 这是一个小的性能提升:
private Task ActivateAsyncTicToc()
{
IsTimerStartAsync = !IsTimerStartAsync;
return AsyncTicToc();
}
事实上,这是一个雷区,我会在这里待一整天。
您需要开始阅读有关async
和await
:
首先,您可以使用await Task.Delay(10)
而不是Thread.Sleep(10)
并将您的AsyncTicToc
方法更改为async Task
而不启动线程。 这将更改 UI 线程(您等待的上下文)中的控件。
其次,要从其他线程更新 UI,如果不想直接使用调度程序,可以使用它的抽象: SynchronizationContext类。 在此处查看更多信息。
示例: SynchronizationContext.Current.Post(action, actionParameter);
第三,使用视图模型和绑定更合适。 如果您使用 WPF,它会为您同步线程以进行属性更改。
虽然可以使用Dispatcher
或SynchronizationContext
从另一个线程“伸出”到 UI 线程,但我强烈建议不要这样做。 这是因为它使您的逻辑更难测试并且更受其环境约束。 在Dispatcher
的情况下,它与在 WPF 环境中运行紧密相关; SynchronizationContext
更好,但它仍然依赖于在某种环境中运行。
使用这种方法,您的逻辑与 UI 线程之间存在依赖关系,如下所示:
UI 代码 => 后台线程逻辑 => UI 线程
相反,使用后台线程逻辑中的IProgress<T>
和Progress<T>
将进度报告传送回其调用方,后者决定如何显示这些进度报告。 然后你的依赖看起来像这样:
UI 代码 => 后台线程逻辑
并且您的后台线程逻辑不依赖于存在的 UI 线程。 这使其更具可重用性和可测试性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.