我想知道使用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。
答案 0 :(得分:1)
您正在将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
的信息
答案 1 :(得分:0)
首先,您可以使用await Task.Delay(10)
代替Thread.Sleep(10)
,然后将AsyncTicToc
的方法更改为async Task
,而无需启动线程。这将使您可以更改UI线程(等待的上下文)中的控件。
第二,要从其他线程更新UI,如果不想直接使用调度程序,则可以在其上使用抽象:SynchronizationContext类。查看更多信息here。
示例:SynchronizationContext.Current.Post(action, actionParameter);
第三,使用视图模型和绑定更为合适。如果使用WPF,它将为您同步线程以进行属性更改。
答案 2 :(得分:0)
尽管可以使用Dispatcher
或SynchronizationContext
从另一个线程“伸出”到UI线程,但我强烈建议不要这样做。这是因为它使您的逻辑难以测试,并且与环境紧密相关。对于Dispatcher
,它与在WPF环境中运行紧密相关; SynchronizationContext
更好,但仍与在某种环境中运行有关。
通过这种方法,您可以从逻辑上依赖于这样的UI线程:
UI代码=>后台线程逻辑=> UI线程
相反,请使用后台线程逻辑中的IProgress<T>
和Progress<T>
将进度报告传递回其调用方,该调用方决定如何显示这些进度报告。然后您的依赖性如下所示:
UI代码=>后台线程逻辑
,并且您的后台线程逻辑不依赖于存在的UI线程。这使得它更具可重用性和可测试性。