C#:等待变量变为非空

时间:2009-06-03 13:29:46

标签: c# multithreading monitor

.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 个答案:

答案 0 :(得分:4)

这种情况下更好的同步原语:

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

答案 1 :(得分:0)

不对当前线程执行自旋等待删除使用单独线程来填充新表单的全部要点?除非我误解了某些内容,否则你只想同步创建新表单。 (它有什么理由需要驻留在不同的STA中吗?)

答案 2 :(得分:0)

您可以尝试以下操作:使用单个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存在后退出。

答案 3 :(得分:0)

我倾向于将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();
}

答案 4 :(得分:0)

传递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());
}

答案 5 :(得分:-1)

使用静态bool标记表单是否已加载。 它是原子的,所以不需要锁定。

在主代码中只需执行类似

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

真正的问题是你为什么要这样做? 通常你希望主线程运行GUI东西,辅助线程运行辅助代码。 如果你解释为什么需要它,我们可能会想出一个更好的技术。