Это ошибка в операторе .net Monitor / lock или MessageBox.Show ведет себя по-разному? - PullRequest
4 голосов
/ 25 февраля 2009

Представьте, что у вас есть две кнопки в форме выигрыша. Как, по вашему мнению, должно быть поведение, когда пользователь нажимает «кнопку 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 каким-либо другим параметром, он будет выполнять оператор только по одному, другие потоки будут ждать по одному.

Ответы [ 4 ]

4 голосов
/ 25 февраля 2009

Поскольку ваш оператор блокировки выполняется, когда InvokeRequired имеет значение false, все блокировки будут выполняться в одном и том же (основном) потоке. Поэтому замки не будут блокироваться.

Если вы хотите, чтобы MessageBox блокировался, используйте ShowDialog.

2 голосов
/ 25 февраля 2009
  1. блокировка блокируется только в том случае, если блокировка принадлежит другому потоку, допускается блокировка одного и того же объекта из одного и того же потока несколько раз - в противном случае это была бы мгновенная тупиковая ситуация, ведь она блокировала текущий поток во время ожидания для текущей темы.

  2. Control.BeginInvoke не выполняет код в другом потоке, он всегда выполняет код в потоке, перекачивая сообщения для элемента управления, он делает это, отправляя сообщение во входную очередь элемента управления и затем выполняя код при получении сообщения.

из-за 2 ваш код вообще не является многопоточным, все выполняется в одном потоке - и это возвращает нас к 1, когда у вас нет нескольких потоков, блокировка ничего не делает.

0 голосов
/ 25 февраля 2009

Я согласен с Нир. После того как вы измените свою функцию на приведенную ниже, вы можете проверить, что вы работаете в том же потоке (что неудивительно):

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

Итак, поскольку ваш поток пользовательского интерфейса блокирован, он не блокируется. Суть в том, что потоки STA несовместимы с правильным многопоточным программированием.

0 голосов
/ 25 февраля 2009

Я подозреваю, что поток пользовательского интерфейса перекачивает сообщения в течение жизненного цикла MessageBox. Поскольку блокировки являются вновь входящими (и поток пользовательского интерфейса выполняет код каждый раз), это вызывает вышеуказанное. Возможно, попробуйте передать владельца (this) в окно сообщения? (Попробую через секунду ...) .

Вы можете заблокировать это более сильно, но это заблокирует рисование («не отвечает» и т. Д.).

...