微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 在异步任务和UI线程中提升PropertyChanged

在许多博客,教程和MSDN中,我可以读到从非UI线程访问UI元素是不可能的 – 好吧,我会得到一个未经授权的例外.为了测试它,我写了一个非常简单的例子:
// 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?

如上所述,await Task.Delay(2000)在UI线程上执行了Continuation,这使得更新控件成为可能.

is it ok to Raise properties in async Task?

我不确定你的意思是“提升属性”,但如果你的意思是提出一个INotifyPropertyChanged事件,那么是的,可以在UI线程上下文中执行它们.

is it ok to modify ObservableCollecition in async Task,or should I
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?

任务是未来将要完成的工作的承诺.当您从UI线程上下文等待TaskAwaitable时,您仍然在UI线程上运行.在以下情况下,您不在UI线程中:

>您的异步方法当前正在从与UI线程不同的线程(ThreadPool线程或新线程)执行
>使用Task.Run将工作卸载到后台ThreadPool线程.

in the code above in firstButton_Click is it ok to manage UI elements
after awaiting the Task? Am I always back on UI thread?

只要您没有使用ConfigureAwait明确告诉您的代码不返回其当前上下文,您将返回到UI线程(false)

原文地址:https://www.jb51.cc/csharp/99895.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐