为什么我的应用程序在锁定时冻结?
Why does my application freeze on lock?
以下场景:我有一个 class,有两种方法:StartLoop
和 StopLoop
。
StartLoop
启动一个循环,轮询一些数据并将其写入数据结构,之后,它引发一个事件。这会重复直到 StopLoop
被调用。
StopLoop
停止循环并清除数据结构。
现在可能会发生,当循环正在向数据结构写入内容时 StopLoop
被执行并清除数据结构。为了防止同时访问,我做了一个锁。
不幸的是,当我在循环开始后调用 StopLoop
时,我的应用程序挂在锁上。
我的简化代码:
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
}
}
}
在我的 Window(这是一个 WPF 应用程序)上,我将我的事件注册到以下处理程序:
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);
}
为什么我的应用程序在调用 StopLoop
时冻结?我该如何解决?
我会使用 ManualResetEvent
来通知它应该停止的线程。这样你就不需要锁定 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
}
请注意,如果您触发的事件在 UI 线程的上下文中执行任何操作,这可能仍会阻塞。也许您根本不需要 Join
。就个人而言,我会让线程在其循环中暂停一下以降低 CPU 使用率。
您会注意到我还移动了锁,因此只覆盖了对数组的访问。将对事件的调用放在锁中是个坏主意。这势必迟早会造成死锁。另外,规则是尽快锁定资源。
关于您的评论:代码背后的想法是等待处理程序完成(否则我的事件太快以至于处理程序无法处理所有事件)。
这不可能发生。所有附加的事件处理程序都将在 Event(this, new EventArgs());
中执行,并且仅在此之后才会执行下一行代码。此外,锁定根本不会改变此行为。
以下场景:我有一个 class,有两种方法:StartLoop
和 StopLoop
。
StartLoop
启动一个循环,轮询一些数据并将其写入数据结构,之后,它引发一个事件。这会重复直到 StopLoop
被调用。
StopLoop
停止循环并清除数据结构。
现在可能会发生,当循环正在向数据结构写入内容时 StopLoop
被执行并清除数据结构。为了防止同时访问,我做了一个锁。
不幸的是,当我在循环开始后调用 StopLoop
时,我的应用程序挂在锁上。
我的简化代码:
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
}
}
}
在我的 Window(这是一个 WPF 应用程序)上,我将我的事件注册到以下处理程序:
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);
}
为什么我的应用程序在调用 StopLoop
时冻结?我该如何解决?
我会使用 ManualResetEvent
来通知它应该停止的线程。这样你就不需要锁定 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
}
请注意,如果您触发的事件在 UI 线程的上下文中执行任何操作,这可能仍会阻塞。也许您根本不需要 Join
。就个人而言,我会让线程在其循环中暂停一下以降低 CPU 使用率。
您会注意到我还移动了锁,因此只覆盖了对数组的访问。将对事件的调用放在锁中是个坏主意。这势必迟早会造成死锁。另外,规则是尽快锁定资源。
关于您的评论:代码背后的想法是等待处理程序完成(否则我的事件太快以至于处理程序无法处理所有事件)。
这不可能发生。所有附加的事件处理程序都将在 Event(this, new EventArgs());
中执行,并且仅在此之后才会执行下一行代码。此外,锁定根本不会改变此行为。