0

マルチスレッドを使用してアプリケーションを作成しています。アプリケーションには、基本的に UI と、バックグラウンドで何らかの作業を行い、UI を更新するスレッドがあります。フォームを閉じると、フォームクローズ イベントで、ワーカー スレッドに停止を通知します。ただし、何らかの理由でブロックされ、何がブロックされたのかわかりません。以下は私の問題の簡略化されたコードです。実際のコードはもっと複雑です。

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 は終了イベントを実行しており、何もせずに座っています。何もしていないため、バックグラウンド スレッドが待機している 1 つのメソッドを含め、メッセージ ループ内の他のイベントを処理できません。

両方のスレッドが互いに待機しており、生産的な作業は行われていません。これがデッドロックの定義です。それは永遠にそのように座ります。

ただし、これは競合状態であることに注意してください。特定の呼び出しが完了した後、フラグが次にチェックされる前にフォームが閉じられるほど幸運な場合Invoke(これは困難です。これらの操作の間にほとんど時間がかかりません)、プログラムはデッドロックしません。

それを修正する方法については; それは言い難い。例全体はやや不自然です。

  • おそらく、バックグラウンド ワーカーから UI を呼び出す必要はまったくないでしょう。実際に UI 作業を行っていない場合は、おそらくこれを行うべきではありません。

  • クロージング ハンドラーでバックグラウンド ワーカーが終了するのを本当に待つ必要がありますか? できる可能性はありますが、多くの場合そうではありません。

于 2013-05-22T18:50:11.127 に答える