Reputation: 989
Following scenario: I have a class with 2 methods: StartLoop
and StopLoop
.
StartLoop
starts a loop that is polling some data and write it to a data structure, after it, it raises an event. This is getting repeated until StopLoop
is called.
StopLoop
stops the loop and clears the data structure.
Now it can happen, that while the loop is writing something to the data structure StopLoop
gets executed and clears the data structure. To prevent that simultanous access, I do a lock.
Unfortunately, when I call StopLoop
after the loop was started, my application hangs at the lock.
My simplified code:
class Foo
{
public event EventHandler Event;
private bool _runLoop;
private List<int> _fooList = new List<int>() { 0 }; //my data structure
static readonly object _myLocker = new object();
public void StartLoop()
{
_runLoop = true;
new Thread(() =>
{
while (_runLoop)
{
lock (_myLocker)
{
Event(this, new EventArgs()); //call event
_fooList[0] = 1; //do some writing on structure
}
}
}).Start();
}
public void StopLoop()
{
lock (_myLocker) //<--- application hangs here
{
_runLoop = false; //end loop
_fooList = null; //clear structure
}
}
}
On my Window (it's a WPF application) i register my event to following Handler:
void foo_Event(object sender, EventArgs e)
{
//append an "a" for each loop
Dispatcher.BeginInvoke(new Action(() => { uxDisplayTest.Text += "a"; }));
//a "DoEvent" for WPF to keep the window responive
Dispatcher.Invoke(new Action(() => { }), System.Windows.Threading.DispatcherPriority.ContextIdle, null);
}
Why is my application freezing when I am calling StopLoop
? And how can i solve it?
Upvotes: 0
Views: 1386
Reputation: 56697
I'd use a ManualResetEvent
to signal the thread it should stop. That way you don't need to lock in StopLoop
:
private Thread _myThread = null;
private ManualResetEvent _stopThread = new ManualResetEvent(false);
public void StartLoop()
{
_myThread = new Thread(() =>
{
while (!_stopThread.WaitOne(1))
{
Event(this, new EventArgs()); //call event
lock (_myLocker)
{
_fooList[0] = 1; //do some writing on structure
}
}
});
_myThread.Start();
}
public void StopLoop()
{
stopThread.Set();
_myThread.Join();
// Why clear the structure? Rather re-init when restarting the threads
//_fooList = null; //clear structure
}
Please be aware that this may still block in case the event you're firing does anything in the context of the UI thread. Maybe you don't need to Join
at all. Personally, I'd have the thread pause a bit within its loop to lower CPU usage.
You will notice I've also moved the lock so only access to the array is covered. It's a bad idea to put the call to the event in the lock. This is bound to cause deadlocks sooner or later. Also, the rule is to lock resources as shortly as possible.
On your comment: The idea behind the code is to wait until the handlers are finished (otherwise my event is too fast so that the handlers cant process all events).
This can not happen. All attached event handlers will be executed in Event(this, new EventArgs());
and only after that the next line of code will be executed. Also, locking doesn't change this behaviour at all.
Upvotes: 1