// simple text to which TextBlock.Text is bound private string sample = "Starting text"; public string Sample { get { return sample; } set { sample = value; RaiseProperty("Sample"); } } private async void firstButton_Click(object sender,RoutedEventArgs e) { await Job(); // asynchronous heavy job commands.Add("Element"); // back on UI thread so this should be ok? } private async Task Job() { // I'm not on UI Thread ? await Task.Delay(2000); // some other job Sample = "Changed"; // not ok as not UI thread? commands.Add("Element from async"); // also not ok? }
我有一个异步运行的Task.在该任务中,我想要更改我的属性(将引发PropertyChanged)并将元素添加到ObservableCollection.由于它是异步运行,我不应该这样做,但我没有例外,代码工作正常.因此我的疑惑和误解:
>为什么我没有例外?
>在异步任务中引发PropertyChanged是否可以?
>可以在异步任务中修改ObservableCollection,还是应该返回Task< ICollection>并在获得结果后修改ObservableCollection-清除它并填充它?
>什么时候我在UI线程上的任务,什么时候不?
>在firstButton_Click上面的代码中,等待任务后管理UI元素是否可以?我总是回到UI线程吗?
System.Threading.Timer newThreadTimer = new System.Threading.Timer((x) => { Sample = "Changed"; // not UI thread - exception commands.Add("Element from async"); // not UI thread - exception },null,1000,Timeout.Infinite);
在上面的代码中,我的想法是正确的 – 就在第一行或第二行之后,我得到一个例外.但是第一个代码是什么?我的Task在UI线程上运行只是运气吗?
我怀疑这是非常基本的事情和我的误解,但我需要一些澄清,因此这个问题.
解决方法
SynchronizationContext
(特别是在
TaskAwaiter
的Task的情况下).然后将连续编组回到SynchronizationContext以执行方法的其余部分(await关键字之后的部分).
让我们看看你的代码示例:
private async Task Job() { // I'm not on UI Thread ? await Task.Delay(2000); // some other job Sample = "Changed"; // not ok as not UI thread? commands.Add("Element from async"); // also not ok? }
当您等待Task.Delay(2000)时,编译器会隐式捕获SynchronizationContext,它当前是您的WindowsFormsSynchronizationContext.当await返回时,继续在同一个上下文中执行,因为你没有明确告诉它不是,你的UI线程.
如果您将代码更改为等待Task.Delay(200).ConfigureAwait(false),则不会将延迟编组回当前的SynchronizationContext,并且会运行ThreadPool线程,导致UI元素更新引发异常.
在您的计时器示例中,Elapsed事件是通过ThreadPool线程引发的,因此您获得一个异常,即您正在尝试更新由不同线程控制的元素.
现在,让我们逐一回答你的问题:
why don’t I get exception?
@H_404_39@如上所述,await Task.Delay(2000)在UI线程上执行了Continuation,这使得更新控件成为可能.
is it ok to Raise properties in async Task?
@H_404_39@我不确定你的意思是“提升属性”,但如果你的意思是提出一个INotifyPropertyChanged事件,那么是的,可以在UI线程上下文中执行它们.
is it ok to modify ObservableCollecition in async Task,or should I
@H_404_39@
return Task and after obtaining the result modify Observable – Clear
it and Fill it?如果您有异步方法并且想要更新UI绑定元素,请确保在UI线程上封送延续.如果从UI线程调用该方法并等待其结果,则将在UI线程上隐式运行continuation.如果您想通过Task.Run将工作卸载到后台线程并确保在UI上运行继续,您可以使用TaskScheduler.FromCurrentSynchronizationContext()捕获SynchronizationContext并显式传递延续
when am I in Task on UI thread and when not?
@H_404_39@任务是未来将要完成的工作的承诺.当您从UI线程上下文等待TaskAwaitable时,您仍然在UI线程上运行.在以下情况下,您不在UI线程中:
>您的异步方法当前正在从与UI线程不同的线程(ThreadPool线程或新线程)执行
>使用Task.Run将工作卸载到后台ThreadPool线程.in the code above in firstButton_Click is it ok to manage UI elements
@H_404_39@
after awaiting the Task? Am I always back on UI thread?只要您没有使用ConfigureAwait明确告诉您的代码不返回其当前上下文,您将返回到UI线程(false)