ObservableCollection内部的异步任务

本文关键字:任务 异步 内部 ObservableCollection | 更新日期: 2023-09-27 17:49:14

我在一个任务中有一个ObservableCollection的奇怪行为。

运行正常:

    private string status;
    public string Status
    {
        get { return status; }
        set { SetField<string>(ref status, value); }
    }
    public void AddRandomTask()
    {
        Task t = Task.Run(async () =>
        {
            await Randomizer.Instance.LongRandAsync (new MainViewModelObserver(this));
                });
                t.ContinueWith(m => 
                    {
                        string s = "done: " + DateTime.Now.ToString("HH:mm:ss");
                        Status = s;
                    }, TaskScheduler.FromCurrentSynchronizationContext());
                Tasks.Add(t);       
                Msg.Add("Task is added");
            }

失败,有两个例外:

构造函数 中的

Msg = new ObservableCollection<string>();

和进一步指出:

public ObservableCollection<string> Msg { get; private set; }
public void AddRandomTask()
        {
            Task t = Task.Run(async () =>
            {
                await Randomizer.Instance.LongRandAsync(new MainViewModelObserver(this));
            });
            t.ContinueWith(m => 
                {
                    string s = "done: " + DateTime.Now.ToString("HH:mm:ss");
                    this.Msg.Add(s);
                }, TaskScheduler.FromCurrentSynchronizationContext());
            Tasks.Add(t);       
            Msg.Add("Task is added");
        }

我错过了什么,为什么字符串属性不同于ObservableCollection?

编辑:例外:

A first chance exception of type 'System.NotSupportedException' occurred in PresentationFramework.dll
A first chance exception of type 'System.NotSupportedException' occurred in mscorlib.dll

ObservableCollection内部的异步任务

如果没有一个最小的、可复制的例子,很难确切地告诉我们发生了什么;但我怀疑问题是你的代码正在改变一个可观察的集合是数据绑定到一个UI元素。

作为一般规则,我总是将ViewModel属性(包括可观察集合)视为具有UI亲和性。

为什么字符串属性不同于ObservableCollection?

我假设你的意思是你发现你可以从后台线程更改字符串属性并使其工作,而从后台线程更改ObservableCollection会抛出异常。嗯,WPF试图通过在后台为简单属性执行一些自动线程封送处理来简化操作。最新版本对集合也有一定程度的支持。

但是我建议你不要用它。首先,WPF是唯一支持这一点的XAML平台。Windows Store、Universal apps、Xamarin Forms……没有人知道,即使是简单的属性。同样,保持你的UI(以及UI的逻辑表示——ViewModel)与后台处理代码的分离将使你的代码更具可移植性,同时鼓励关注点的分离,并且更加简洁。

所以,在我看来,最好的方法就是不要从后台线程更新ViewModels。在您的情况下,看起来Task.Run是多余的,所以这是一个相对容易的修复:

public void AddRandomTask()
{
  Task t = ProcessAsync();
  Tasks.Add(t);       
  Msg.Add("Task is added");
}
private async Task ProcessAsync()
{
  await Randomizer.Instance.LongRandAsync(new MainViewModelObserver(this));
  string s = "done: " + DateTime.Now.ToString("HH:mm:ss");
  this.Msg.Add(s);
}

还注意使用单独的async方法,这消除了显式ConfigureAwait/SynchronizationContext的需要。

这不是失败,也不是bug,这是一个特性。

第一次机会异常意味着我的代码有问题吗?

第一次异常消息通常并不意味着存在代码中有问题。的应用程序/组件异常优雅,第一次机会异常消息让开发人员知道遇到了一个异常情况处理。

http://blogs.msdn.com/b/davidklinems/archive/2005/07/12/438061.aspx