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

时间:2015-03-30 11:40:35

标签: c# wpf multithreading locking

以下场景:我有一个包含2种方法的课程: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
        }
    }
}

在我的窗口(它是一个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时我的申请会冻结?我该如何解决?

1 个答案:

答案 0 :(得分:1)

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