C #: дождаться, пока переменная станет ненулевой - PullRequest
2 голосов
/ 03 июня 2009

Семантика нечетной блокировки .Net снова вызывает у меня проблемы.

Я запускаю поток, дочерний поток по очереди запускает форму. Родительский поток должен дождаться создания формы.

Моей первой попыткой было использование Monitor для просмотра переменной Form:

private void OpenForm()
{
    if (FormThread == null)
    {
        Monitor.Enter(Form);
        FormThread = new Thread(FormStub);
        FormThread.SetApartmentState(ApartmentState.STA);
        FormThread.Start();
        Monitor.Wait(Form);
        Monitor.Exit(Form);
    }
}

private void FormStub()
{
    Form = new ConnectorForm();
    Monitor.Enter(Form);
    Monitor.PulseAll(Form);
    Monitor.Exit(Form);
    Application.Run(Form);
}

... Это исключение. Monitor.Enter () завершается ошибкой, поскольку Form == null.

Я мог бы очень легко создать фиктивное целое число или что-то еще (я действительно думаю, что смогу изменить переменную FormThread), но мне было интересно, есть ли более элегантное решение.

Ответы [ 6 ]

4 голосов
/ 03 июня 2009

Лучший примитив синхронизации для этого случая:

private ManualResetEvent mre = new ManualResetEvent(false);

private void OpenForm()
{
    if (FormThread == null)
    {
        FormThread = new Thread(FormStub);
        FormThread.SetApartmentState(ApartmentState.STA);
        FormThread.Start();
        mre.WaitOne();
    }
}

private void FormStub()
{
    Form = new ConnectorForm();
    mre.Set();
    Application.Run(Form);
}
0 голосов
/ 03 июня 2009

Другой способ передать EventWaitHandle - передать его в FormStub в качестве параметра (чтобы он не загромождал вашу объектную модель):

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    EventWaitHandle e = new EventWaitHandle(false, EventResetMode.ManualReset);
    Thread t = new Thread(FormStub);
    t.SetApartmentState(ApartmentState.STA);
    t.Start(e);
    e.WaitOne();
}

static void FormStub(object param)
{
    EventWaitHandle e = (EventWaitHandle) param;
    Form f = new Form1();

    e.Set();
    Application.Run(new Form1());
}
0 голосов
/ 03 июня 2009

Используйте статический тип bool, чтобы указать, была ли загружена форма. Он атомарный, поэтому не нуждается в блокировке.

В основном коде просто сделайте что-то вроде

while(!formRun) { Thread.Sleep(100); }

Реальный вопрос в том, почему ты это делаешь? Обычно вы хотите, чтобы основной поток выполнял материал с графическим интерфейсом, а дополнительные потоки - вспомогательный код. Если вы объясните, зачем вам это нужно, мы можем придумать лучшую технику.

0 голосов
/ 03 июня 2009

Я склонен использовать AutoResetEvent для таких случаев, как эти:

private AutoResetEvent _waitHandle = new AutoResetEvent(false);

private void OpenForm()
{
    Thread formThread = new Thread(FormStub);
    formThread.SetApartmentState(ApartmentState.STA);
    formThread.Start();
    _waitHandle.WaitOne();

    // when you come here FormStub has signaled                
}

private void FormStub()
{
    // do the work

    // signal that we are done
    _waitHandle.Set();
}
0 голосов
/ 03 июня 2009

Вы можете попробовать следующее, в котором в качестве механизма сообщения используется один object / Monitor:

private void OpenForm()
{
    if (FormThread == null)
    {
        object obj = new object();
        lock (obj)
        {
            FormThread = new Thread(delegate () {
                lock (obj)
                {
                    Form = new ControllerForm();
                    Monitor.Pulse(obj);
                }
                Application.Run(Form);
            });
            FormThread.SetApartmentState(ApartmentState.STA);
            FormThread.Start();
            Monitor.Wait(obj);
        }
    }
}

Исходный поток удерживает блокировку, пока не вызовет Monitor.Wait; это позволяет второму потоку (уже запущенному) создать форму, вернуть исходный поток к жизни и освободить его; исходный поток завершается только после Form.

0 голосов
/ 03 июня 2009

Не выполняет ли ожидание вращения в текущем потоке весь смысл использования отдельного потока для запуска новой формы? Если я чего-то не понимаю, вы просто хотите создать новую форму синхронно. (Есть ли какая-то причина, по которой он должен находиться в другом STA?)

...