从不同的线程访问对象

时间:2011-08-28 19:01:13

标签: c# wpf multithreading

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

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

所以,如果我这样做:

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");

我用方法接收这些事件:

enter image description here

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

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

那么如何通过事件传递对象?


编辑

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

enter image description here

我也收到错误!

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

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

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

    }

我没有收到错误!

为什么我在事件中遇到错误,并且在使用常规按钮调用时没有出错。

2 个答案:

答案 0 :(得分:13)

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

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

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

答案 1 :(得分:1)

问题不在于您尝试对发送给方法的值执行某些操作,问题就是您要尝试使用它。

事件处理程序仍然在后台线程中运行,从那里你不能使用任何UI控件,因为它们属于主线程。

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

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