Frage

I am writing an application using multithread. The application basically has a UI and a thread doing some work in the background and updating the UI. When I close the form, In the formclosing event, I notify the worker thread to stop. However, for some reasons It blocks and I have no ideas what caused it to block. Below is the simplified code of my problem, my actual code is more complicated.

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))
            { 
            }
        }
    }
}

The problem is if I use

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

to stop the worker thread in a button event, the worker thread will stop but not in the form closing event. Any help would be appreciated. Thanks.

War es hilfreich?

Lösung

Your FormClosing event is waiting for the background thread to close before it lets the method end. Note that it will be running in the UI thread.

Your background thread is, in a loop, invoking a method in the UI thread and, since you use Invoke rather than BeginInvoke, it is waiting for that method to complete before continuing.

The UI is running the closing event, sitting there doing nothing. Since it's doing nothing, it can't process any of the other events in the message loop, including the one method that the background thread is waiting on.

Both threads are each waiting on each other, and no productive work is being done. This is the definition of a deadlock. It will sit that way forever.

Note that this is a race condition though; if you're lucky enough for the form to be closed after a given Invoke call completes and before the flag is next checked (which is hard; it spends very little time between those operations) then your program won't deadlock.

As for how to fix it; that's hard to say. The whole example is somewhat contrived.

  • Perhaps you don't need to be invoking to the UI at all from the background worker; if you're not actually doing UI work, you probably shouldn't be doing this.

  • Do you really need to wait for the background worker to finish in your closing handler? It's possible that you do, but often you wouldn't.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top