【问题标题】:access object from a different thread从不同的线程访问对象
【发布时间】:2011-08-28 19:01:12
【问题描述】:

我有一个服务器类,它基本上等待来自客户端的连接。在该类中,我创建了一个 NetworkStream 对象,以便能够从客户端接收字节。因为 NetworkStream.Read() 方法不是异步的(这意味着它将等到它从客户端读取字节以便继续执行类似于 messagebox 方法的代码),我必须在单独的线程中读取字节,以便如果程序恰好在等待读取数据,使用该程序的用户仍然可以与程序交互。

无论如何,该线程拥有很多对象。一个例子是我在那个类中有一个名为 log 的列表。我使用该列表来了解服务器的状态。也许它正在侦听连接,或者它的状态是“已连接”或“已断开”。

所以如果我这样做:

Server myServer = new Server("192.168.0.120","1300"...\\ I pass the appropite parameters in order to instantiate it

//...
.. then I am able to latter look at the log as
string foo = myServer.Log[0] for example.

因为我想知道日志何时更新,所以在服务器类上我创建了一个事件:

    public delegate void onUpdateHandler(string newStatus);
    public event onUpdateHandler onUpdate = delegate { };

然后我在 Server 类上触发事件:

 onUpdate("waitingForConnection");

我使用以下方法接收这些事件:

但如果我尝试使用 newStatus 做某事,我会收到错误说明:

System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.

那么我怎样才能传递一个带有事件的对象呢?


编辑

所以我也注意到,如果我这样做:

我也遇到了错误!

但是当我做同样的事情时,从一个按钮调用它:

   // SERVER IS RUNNING BEFORE CALLING THIS METHOD
    private void button3_Click(object sender, RoutedEventArgs e)
    {

        listView1.Items.Add("my own string");

    }

我没有收到错误消息!

为什么我的事件出错,而使用常规按钮调用它时却没有出错。

【问题讨论】:

    标签: c# wpf multithreading


    【解决方案1】:

    问题是线程试图访问 ListView 这是一个 DependencyObject 具有线程亲和力,使用 Dispatcher 在 UI 线程上执行这样的方法,例如:

    Application.Current.Dispatcher.Invoke((Action)(() =>
    {
        listView1.Items.Add(newStatus);
    }));
    

    另请参阅threading model reference 了解更多信息。

    【讨论】:

    • 当我将它放在我的方法中时,我收到一条错误消息:无法将 lambda 表达式转换为类型“System.Delegate”,因为它不是委托类型
    • @TonoNam:哦,是的,你需要强制转换它,或者明确地创建一个动作。
    • @Tono,必须将 lambda 转换为某种委托类型,请参阅我的编辑。
    • @svick:忘记了,因为我通常有自己的Dispatch-方法,它直接使用Action 作为参数,导致隐式转换。
    • @H.B.谢谢您的帮助!传入我创建的委托时效果很好。但是为什么你必须这样做呢?我在哪里可以获得有关此的更多信息?我想了解它,而不仅仅是在知道它工作的情况下使用它而不知道它是如何工作的......
    【解决方案2】:

    问题不在于你试图用你发送给方法的值做某事,问题在于你试图用它做什么。

    事件处理程序仍在您的后台线程中运行,因此您无法使用任何 UI 控件,因为它们属于主线程。

    通常的处理方式是使用CheckAccess方法检查是否需要切换线程,使用Invoke方法将工作交给主线程:

    void server_onUpdate(string newStatus) {
      if (!listView1.Dispatcher.CheckAccess()) {
        listView1.Dispatcher.Invoke(server_onUpdate, newStatus)
      } else {
        listView1.Items.Add(newStatus);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多