想象一下,获胜表单上有两个按钮。您认为当用户使用下面的代码按下“按钮 1”时应该有什么行为?

它应该一次性显示所有 5 个消息框,还是一一显示 - MessageBox.Show 语句位于锁定语句内?

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private static readonly object lockobject = new object();

    private void button1_Click(object sender, EventArgs e)
    {
        var action = new Action(function);
        for(int i = 0; i< 5; i++)
        {
            action.BeginInvoke(null, null);
        }
    }

    private void function()
    {
        if (button2.InvokeRequired)
        {
            var func = new Action(function);
            button2.Invoke(func);
        }
        else
        {
            lock (lockobject)
            {
                MessageBox.Show("Testing");
            }
        }
    }
}

现在,如果我们将 MessageBox.Show 替换为任何其他语句,它将一次只执行一个语句,其他线程将等待,一次一个。

有帮助吗?

解决方案

由于当InvokeRequired为假时,执行你的lock语句,锁会在同一个(主)线程中运行。因此,锁将不阻塞。

如果要在MessageBox阻断,使用的ShowDialog代替。

其他提示

  1. lock 仅在另一个线程拥有该锁时才会阻塞,允许同一线程多次锁定同一对象 - 否则这将是一个即时死锁,毕竟它会在等待当前线程时阻塞当前线程。

  2. Control.BeginInvoke 不在不同的线程中执行代码,它将始终执行为控件泵送消息的线程中的代码,它通过将消息发布到控件的输入队列,然后在消息到达时执行代码来实现。

因为 2 你的代码根本不是多线程的,所有东西都在同一个线程中执行 - 这让我们回到了 1,当你没有多线程锁时什么也不做。

我怀疑UI线程在MessageBox生命周期期间泵送消息。因为锁被重入(和UI线程每次运行代码),这会导致上述情况。 也许尝试使所述所有者(this)到消息框? (我将尝试在秒...)

您可以更有力地阻止它,但是这将阻止画(“没有响应”等)。

我同意尼尔。你改变你的函数下面的一个后,你可以测试你是在同一个线程上运行(这并不奇怪):

private void function()
{
   if (button2.InvokeRequired)
   {
        var func = new Action(function);
        button2.Invoke(func);
   }
   else
   {
        lock (lockobject)
        {
            int threadId = Thread.CurrentThread.ManagedThreadId;
            MessageBox.Show("Testing. Running on thread "+threadId);
        }
    }
}

所以在这里,因为你的UI线程是由于锁,它不会阻止。底线是STA线程不是具有适当的多线程编程兼容。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top