我有一个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");
我用方法接收这些事件:
但如果我尝试用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");
}
我没有收到错误!
为什么我在事件中遇到错误,并且在使用常规按钮调用时没有出错。
答案 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);
}
}