C#Application.DoEvents无法在我的应用程序中运行

时间:2014-02-12 09:31:21

标签: c# multithreading winforms synchronizationcontext

由于我的英语不太好,我会简单解释一下我的问题并在此处粘贴一段代码片段来描述问题。

问题是我们的winForm应用程序中的多线程问题。我将逻辑简化为以下代码示例。

在测试代码中,mainForm中有1个mainForm Form1和一个名为“Start”的按钮。当用户单击该按钮时,将从2个后台线程中显示两个表单form2和form3。在关闭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);
    }
}
}

2 个答案:

答案 0 :(得分:2)

第一个建议:不要使用Application.DoEvents()。永远。无论何时您认为自己需要它,您的代码流中都存在一个应该首先解决的概念性问题。我猜你的代码只是创建一个死锁,因为它等待OnClosing回调返回之前它可以处理更多的事件(比如关闭另一个表单)。

答案 1 :(得分: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);
            }));
        }
    }