C#:等待变量变为非空

发布于 2024-07-24 07:02:50 字数 720 浏览 2 评论 0原文

.Net 奇怪的锁定语义再次困扰着我。

我正在启动一个线程,子线程依次启动一个表单。 父线程应该等待,直到创建表单。

我的第一次尝试是使用监视器来监视 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 变量),但我想知道是否有更优雅的解决方案。

.Net's odd locking semantics are bugging me again.

I'm launching a thread, the child thread in turns starts a form. The parent thread should wait until the form is created.

My first attempt was to use a Monitor to watch the Form variable:

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

... This throws an exception. Monitor.Enter() fails, since Form == null.

I could very easily create a dummy integer or something (I actually think I'll canabalize the FormThread variable), but I was wondering if there was a more elegant solution.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

彻夜缠绵 2024-07-31 07:02:50

对于这种情况,更好的同步原语:

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

Better synchronisation primitive for this case:

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);
}
超可爱的懒熊 2024-07-31 07:02:50

在当前线程上执行旋转等待是否会删除使用单独线程来启动新表单的全部意义? 除非我误解了什么,否则您只想同步创建新表单。 (有什么理由需要将其驻留在不同的 STA 中吗?)

Doesn't performing a spin-wait on the current thread delete the whole point of using a separate thread to lanch the new form? Unless I'm misunderstanding something, you just want to create the new form synchronously. (Is there any reason it needs to reside in a different STA?)

要走干脆点 2024-07-31 07:02:50

您可以尝试以下方法,它使用单个 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 存在后,原始线程才会退出。

You could try the following, which uses a single object/Monitor as the message mechanism:

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

The original thread holds the lock until it calls Monitor.Wait; this lets the second thread (already started) in to create the form, pulse the original thread back into life, and release; the original thread then exits only after Form exists.

不羁少年 2024-07-31 07:02:50

对于类似的情况,我倾向于使用 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();
}

I tend to use the AutoResetEvent for cases like these:

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();
}
何处潇湘 2024-07-31 07:02:50

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

Another way to pass an EventWaitHandle is to pass it to FormStub as a parameter (so it does not clutter your object model):

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());
}
转身泪倾城 2024-07-31 07:02:50

使用静态布尔值来标记表单是否已加载。
它是原子的,因此不需要锁定。

在主代码中只需执行类似的操作,

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

真正的问题是你为什么要这样做?
通常您希望主线程运行 GUI 内容,而辅助线程运行帮助程序代码。
如果您解释为什么需要它,我们也许可以想出更好的技术。

Use a static bool to flag whether or not the Form has loaded.
It is atomic so won't need locking.

In the main code just do something like

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

The real question is why are you doing this?
Usually you want the main thread to run GUI stuff, and secondary threads to run helper code.
If you explain why you need it we can maybe come up with a better technique.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文