C# C“TaskFactory.StartNew Changed DataSource导致System.InvalidOperationException”;“从另一个线程访问”;

C# C“TaskFactory.StartNew Changed DataSource导致System.InvalidOperationException”;“从另一个线程访问”;,c#,multithreading,data-binding,datagridview,C#,Multithreading,Data Binding,Datagridview,我目前正在构建一个应用程序,它依赖DataGridView通过WAMP协议接收更新,这涉及处理新的消息事件。我的事件处理程序如下所示: private async void NewMessage(object sender, MessageEventArgs e) { await Task.Factory.StartNew(() => DataHolder.TableSource.Add(new CustomData(e.Name, e.Surname,

我目前正在构建一个应用程序,它依赖DataGridView通过WAMP协议接收更新,这涉及处理新的消息事件。我的事件处理程序如下所示:

 private async void NewMessage(object sender, MessageEventArgs e)
 {
      await Task.Factory.StartNew(() => DataHolder.TableSource.Add(new 
      CustomData(e.Name, e.Surname, 
      e.Whatever, e.WhoTheHellCares)));
 }
发生此类事件时,会引发异常:
System.invalidoOperationException
“试图访问在不同线程中创建的控制元素”

DataHolder
是一个静态类,与具有此事件处理程序的表单类
DataHolder存在于同一命名空间中。TableSource
是一个绑定在
form中的
BindingList
。将
事件加载到在Form1.Designer.cs中创建的
DataGridView
控件中

我在这里读到了一篇相关的文章,其中提到了
await
能够在需要时自动将某些内容打包到UI线程,但我的猜测是
await
无法识别数据绑定,因此必须明确告诉它这样做,如何

我需要一个.net 4.5解决方案,或者证明任务和等待无法解决我的问题。只是我很难将它应用到我自己的情况中

更新

哇,见鬼。。即使我的处理程序看起来像这样,它仍然会给我相同的异常

 private void NewMessage(object sender, MessageEventArgs e)
 {
      DataHolder.TableSource.Add(new 
      CustomData(e.Name, e.Surname, 
      e.Whatever, e.WhoTheHellCares));
 }
我猜这与触发事件的类本身有关。好。。我试过D:

更新
我已经使用了调试工具,这里是交易-事件本身已经嵌套在另一个线程中。将尝试重写我正在使用的库的源代码,以便它支持进度报告。祝我好运,D:

你在误解异步/等待

您的方法将分为两部分:等待前的和等待后的。因此,当代码运行时,它将如下执行:

 private async void NewMessage(object sender, MessageEventArgs e)
 {
      await Task.Factory.StartNew(() => DataHolder.TableSource.Add(new 
      CustomData(e.Name, e.Surname, 
      e.Whatever, e.WhoTheHellCares)));
 }
  • 之前等待
  • 在异步线程中等待
  • 在另一个线程中执行等待的方法(Task.StartNew中的内容)(不一定)
  • 当步骤3完成时,它将唤醒(封送)步骤2的执行
  • 在等待后运行
  • 所以现在发生的事情是,被封送到UI线程的是方法的第二部分的执行,而不是任务内部的部分

    还有比这更多的东西,但我想这是我用来理解它的步骤

    要解决此问题,请完全删除async,因为您没有执行长流程:

    private void NewMessage(object sender, MessageEventArgs e)
    {
         DataHolder.TableSource.Add(new CustomData(e.Name, e.Surname, 
         e.Whatever, e.WhoTheHellCares));
    }
    
    或者,如果您希望异步并且处于WinForms控件中,请使用BeginInvoke

    private void NewMessage(object sender, MessageEventArgs e)
    {
        this.BeginInvoke((Action)(() => DataHolder.TableSource.Add(new 
          CustomData(e.Name, e.Surname, 
          e.Whatever, e.WhoTheHellCares))));
    }
    
    试图访问在不同线程中创建的控制元素

    问题是,您的代码正在从主UI线程以外的其他对象接触UI元素(或它所依赖的对象)您不能这样做。

    我知道您不想使用
    Invoke
    ,但这确实是解决这个问题的方法。以下链接(以及数百个类似链接)讨论了这一问题:


    根据你的评论,你可以考虑另一种方法。看看它是如何使用SynchronizationContext的。

    如果要从另一个线程访问UI线程,必须使用Dispatcher或某些Windows窗体。等待能够自动将某些内容打包到UI。。我想你没有抓住你读到的要点。。这是针对
    await
    (如果您没有使用
    ConfigureAwait(false)
    )之后的代码,您在哪里更新数据源?我们看不到。为什么不异步获取数据并等待它呢。在那之后,你根据结果更新你的数据源?顺便说一句,你引用的@TorlanDelta的可能重复项被破坏了。他甚至没有在不同的线程上运行任何东西!没有
    Task.Run
    Task.Factory.StartNew
    。所以没有其他线索,这就是为什么他在他的案例中没有得到那个错误。是.NET4.5解决方案。我只是不知道如何把它应用到我的问题上。老实说,我已经浏览了很多这样的问题,我对他们给出的解决方案不满意,因为这些解决方案已经过时了。请看我最近的评论。最终,他们的建议是使用一个
    SynchronizationContext
    ,它(在其抽象背后)将调用它,这与调用
    Invoke
    基本相同。请参阅。谢谢,我确实需要一些更实际的异步编程解释,但我现在遇到的问题可以用这个来回答,我只是不知道如何实现我自己的问题。请从方法签名中删除异步。从方法中删除wait,不要使用任务。这里不需要这样做,因为您没有运行很长的进程,只需更新数据源即可。请检查更新后的xD Oops。。我一直在使用这个简单的例子作为起点,因为是的,这不是一个长时间运行的任务,但我的应用程序肯定会有另一个datagridview或一个图表,这将涉及一些困难的计算。从方法签名中删除async。“无法将lambda表达式转换为委托类型,因为它不是委托类型”这是编译器对您的建议D所说的: