0

私は英語が苦手なので、質問を簡単に説明し、ここにコード スニペットを貼り付けて問題を説明します。

問題は、winForm アプリケーションのマルチスレッドの問題です。次のコードサンプルのようにロジックを単純化します。

テスト コードには、1 つの mainForm Form1 と、mainForm 内に「Start」という名前のボタンがあります。ユーザーがボタンをクリックすると、2 つのフォーム form2 と form3 が 2 つのバックグラウンド スレッドから表示されます。form2 が閉じられた後、Form1 が閉じられます。ただし、ここには form3 が表示されているため、ユーザーが自分で form3 を閉じる必要があります。そこで、form.Closing イベントを処理し、Application.DoEvents() を使用して、ユーザーが form3 を閉じられるようにしました。それは念頭に置いているように見えます。しかし実際には、form3 はユーザーのアクションを受け入れることができますが、form3 は期待どおりに閉じられません。

ここで form3 を閉じることができない理由と、ユーザーの閉じる操作が機能するようにコードを変更する方法を説明してください。

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace CloseFormFromMainThread
{
public partial class Form1 : Form
{
    private Form2 _form2;
    private Form2 _form3;
    private SynchronizationContext _synchronizationContext;

    public Form1()
    {
        InitializeComponent();
        Closing += Form1Closing;
    }

    void Form1Closing(object sender, CancelEventArgs e)
    {
        while (_form3 != null)
        {
            Application.DoEvents();
            Thread.Sleep(100);
        }
    }

    private void ButtonStartClick(object sender, EventArgs e)
    {
        var thread = new Thread(StartForm3);
        thread.Start();

        var thread2 = new Thread(StartForm2);
        thread2.Start();
    }

    private void StartForm3()
    {
        Thread.Sleep(200);
        var action = new Action(() =>
                                    {
                                        _form3 = new Form2();
                                        _form3.Text = "form 3";
                                        _form3.ShowDialog();
                                        _form3 = null;
                                    });
        ExecuteActionInUiThread(action);
    }

    private void Form1Load(object sender, EventArgs e)
    {
        _synchronizationContext = SynchronizationContext.Current;
    }

    private void StartForm2()
    {
        Thread.Sleep(500);
        var action = new Action(() =>
        {
            _form2 = new Form2();
            _form2.Text = "form 2";
            _form2.ShowDialog();

            Close();
        });
        ExecuteActionInUiThread(action);
    }

    private void ExecuteActionInUiThread(Action action)
    {
        var sendOrPostCallback = new SendOrPostCallback(o => action());
        _synchronizationContext.Send(sendOrPostCallback, null);
    }
}
}
4

2 に答える 2

2

最初の提案: Application.DoEvents() を使用しないでください。これまで。必要だと思うときはいつでも、コード フローに概念的な問題があり、最初に修正する必要があります。あなたのコードは、(別のフォームを閉じるなどの) さらに多くのイベントを処理できるようになる前に、OnClosing コールバックが戻るのを待っているため、デッドロックを作成しているだけだと思います。

于 2014-02-12T09:51:12.340 に答える
0

Form.Close() のソース コードを確認したところ、モーダル ダイアログでは FormClosing イベントのみが発生し、Closed イベントは発生しませんでした。form3.ShowDialog を進めるための信号は送信されませんでした。したがって、メインスレッドのみを使用すると思います.Application.DoEventsはコードを先に進めることができません. 次に、メイン スレッドでのソフト デッドロックです。

現在、別のスレッドを使用してチェック (_form3 != null) を実行し、メイン スレッドに _form3.ShowDialog() ロジックを実行させています。以下は、Form1Closing に関する私のコードです。

    private bool _isFormClosing;
    void Form1Closing(object sender, CancelEventArgs e)
    {
        if (_form3 == null) return;

        e.Cancel = true;
        if (!_isFormClosing)
        {
            _isFormClosing = true;
            Task.Factory.StartNew((() =>
            {
                while (_form3 != null)
                {
                    Thread.Sleep(50);
                }

                ExecuteActionInUiThread(Close);
            }));
        }
    }
于 2014-02-15T03:51:14.067 に答える