Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

access object from a different thread

I have a Server class which it basically waits for connections from a client. Inside that class I create an NetworkStream object in order to be able to receive bytes from a client. Because the NetworkStream.Read() method is not asynchronous (meaning that it will wait until it reads bytes from a client in order to proceed executing code similar to the messagebox method), I have to read for bytes in a separate thread so that the user using the program can still interact with the program if the program happens to be waiting to read for data.

anyways a lot of objects are owned by that thread. One example is that I have a List called log in that class. I use that list to know the status of the server. Maybe it is listening for a connection or perhaps it's status is "connected" or "disconnected".

So if I do something like:

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.

because I want to know when the log is updated, on the server class I have created an event as:

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

I then fire events on the Server class as:

 onUpdate("waitingForConnection");

and I receive those events with the method:

enter image description here

but if I try to do something with newStatus I get the error stating:

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

so how can I pass an object with an event?


Edit

so I also notice that if I do:

enter image description here

I also get an error!

but when I do the same thing calling that from a button as:

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

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

    }

I do NOT get an error!

why is it that I get an error with the event and I do not get an error when calling it with a regular button.

like image 270
Tono Nam Avatar asked Aug 28 '11 19:08

Tono Nam


2 Answers

The problem is that the thread tries to access the ListView which is a DependencyObject which has thread affinity, use the Dispatcher to execute methods like this on the UI-thread, e.g.:

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

Also see the threading model reference for additional info.

like image 140
H.B. Avatar answered Oct 24 '22 02:10

H.B.


The problem is not that you try to do something with the value that you sent to the method, the problem is what you are trying to do with it.

The event handler is still running in your background thread, and from there you can't use any UI controls as they belong to the main thread.

The usual way of handling that is to use the CheckAccess method to check if you need to switch treads, and the Invoke method to hand off the work to the main thread:

void server_onUpdate(string newStatus) {
  if (!listView1.Dispatcher.CheckAccess()) {
    listView1.Dispatcher.Invoke(server_onUpdate, newStatus)
  } else {
    listView1.Items.Add(newStatus);
  }
}
like image 1
Guffa Avatar answered Oct 24 '22 02:10

Guffa