0

我正在使用多线程编写应用程序。该应用程序基本上有一个 UI 和一个在后台执行一些工作并更新 UI 的线程。当我关闭表单时,在 formclosure 事件中,我通知工作线程停止。但是,由于某些原因,它阻塞了,我不知道是什么原因导致它阻塞。下面是我的问题的简化代码,我的实际代码更复杂。

namespace CmdTest
{
    public partial class Form1 : Form
    {
        Thread _workerThread;
        static object _lock;
        static bool _stopFlag;

        public Form1()
        {
            _lock = new object();
            _stopFlag = false;
            _workerThread = new Thread(new ThreadStart(ThreadDoWork));

            InitializeComponent();

            _workerThread.Start();
        }

        delegate void UpdateUI();
        public void UpdateUICallback()
        {
            //Doing stupid things
            int i = 0;
            while (i < 10000)
            {
                i++;
            }
        }

        public void ThreadDoWork()
        {

            if (this.InvokeRequired)
            {
                UpdateUI updateUI = new UpdateUI(UpdateUICallback);
                while (true)
                {
                    //telling the UI thread to update UI.
                    this.Invoke(updateUI);

                    lock (_lock)
                    {
                        if (_stopFlag)
                            return;
                    }
                }
            }
        }       

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            //tell the worker thread to terminate.
            lock (_lock)
            {
                _stopFlag = true;
                Monitor.Pulse(_lock);
            }

            while (!_workerThread.Join(0))
            { 
            }
        }
    }
}

问题是如果我使用

lock (_lock)
{
   _stopFlag = true;
   Monitor.Pulse(_lock);
}

要在按钮事件中停止工作线程,工作线程将停止但不会在表单关闭事件中停止。任何帮助,将不胜感激。谢谢。

4

1 回答 1

3

您的FormClosing事件正在等待后台线程关闭,然后才让方法结束。请注意,它将在 UI 线程中运行。

您的后台线程在循环中调用 UI 线程中的方法,并且由于您使用Invoke而不是BeginInvoke,它正在等待该方法完成,然后再继续。

UI 正在运行关闭事件,坐在那里什么也不做。由于它什么都不做,它不能处理消息循环中的任何其他事件,包括后台线程正在等待的一个方法。

两个线程都在互相等待,并且没有进行任何生产性工作。这是死锁的定义。它将永远坐在那里。

请注意,这是一个竞争条件;如果您足够幸运,在给定Invoke调用完成后和下一次检查标志之前关闭表单(这很难;在这些操作之间花费的时间很少),那么您的程序就不会死锁。

至于如何修复它;这很难说。整个例子有点做作。

  • 也许您根本不需要从后台工作人员调用 UI;如果您实际上并没有做 UI 工作,那么您可能不应该这样做。

  • 您真的需要等待后台工作人员在您的关闭处理程序中完成吗?您可能会这样做,但通常不会。

于 2013-05-22T18:50:11.127 回答