为什么我的应用程序在锁定时冻结?

Why does my application freeze on lock?

以下场景:我有一个 class,有两种方法:StartLoopStopLoop

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()); 中执行,并且仅在此之后才会执行下一行代码。此外,锁定根本不会改变此行为。