Race status when using an observer template with a graphical interface

I am new to C # and thread, and recently I started working on a utility that uses multiple threads. I have an event processing logic executed by a single thread, and then a GUI in a separate thread that monitors the event handler and receives notifications when new events are received.

When the GUI is manually closed by the user, I disconnect it so that it no longer observes the event handler. However, the next time the event handler receives the event, he thinks he still has something on this observer list. I added some printouts / breakpoints and it seems to go into NotifyObservers and fall into the foreach loop, then go to the detach method, empty the list of watchers, and then when it returns to NotifyObservers, the watcher it is trying to access has already been deleted and it gets an exception.

I saw on this page that you should use locks to prevent race conditions from occurring, and I tried to use them in the observer list before foreach in NotifyObservers and it still gets an exception. I think this may be due to a blockage of the inability to prevent the GUI from closing on another thread, so the other thread does not wait for me to try to block, but I'm new to this, so I'm not quite sure. I tried to throw a bunch of other locks in these methods, and nothing worked.

I have included the code for the 3 methods described below, Detach and NotifyObservers are in my event handler, and HandleClosing is in my browser

protected void HandleClosing(object sender, EventArgs e) { handler.Detach(this); } public void Detach(SubscriberObserver observer) { observers.Remove(observer); } public void NotifyObservers() { foreach (SubscriberObserver observer in observers) { observer.Invoke(new Action(() => { observer.Notify(); })); } } 
+4
source share
1 answer

I do not know what collection of observers you have, but I assume that it is a kind of thread-safe collection, which can behave as follows when iterating through the foreach loop. It locks itself, then creates an IEnumerable copy of itself, and then unlocks. Then the iteration begins over the elements of the copy. If you delete an item from the collection after creating the copy, it does not matter, the loop will still encounter the deleted item.

To fix the race condition, you need to lock the collection for the entire iteration, and also delete within the lock on the same object. You can create a lock object for this single purpose, or you can lock ICollection.SyncRoot if your collection implements this.

If observer is Control == true , you may encounter a dead end when invoking Invoke . Instead, try calling BeginInvoke . Quote from MSDN : "The difference between the two methods is that the Invoke call is a blocking call, while the BeginInvoke call is not. In most cases, it is more efficient to call BeginInvoke, because the secondary thread can continue to execute without waiting for the main the user interface thread will complete its work by updating the user interface.

0
source

Source: https://habr.com/ru/post/1393858/


All Articles