使用 Dispatcher.Run() 时如何避免竞争条件?

发布于 2024-11-04 13:56:46 字数 556 浏览 0 评论 0原文

我发现有关如何正确使用 Dispatcher 类本身的信息很少。

目前我正在使用它类似于这个问题,但有一个我在任何地方都没有看到提到的固有竞争条件。

假设您使用以下代码来启动调度程序线程:

Thread thread = new Thread(Dispatcher.Run);
thread.Start();

并稍后尝试使用它:

Dispatcher.FromThread(thread).Invoke(MyMethodDelegate);

这通常会抛出 NullReferenceException,因为 Dispatcher.FromThread 调用可能会返回 null,因为无法保证已调用 Dispatcher.Run。

为了正确实现这一点,我所做的就是使用信号来确保调度程序正在运行,然后再继续在主线程上使用它。

I've found little information on how to properly use the Dispatcher class on its own.

Currently I am using it similar to this question, but there is an inherent race condition which I do not see mentioned anywhere.

Assuming you use the following code to start a dispatcher thread:

Thread thread = new Thread(Dispatcher.Run);
thread.Start();

And try to use it later:

Dispatcher.FromThread(thread).Invoke(MyMethodDelegate);

This will often throw a NullReferenceException as the Dispatcher.FromThread call may return null since there is no guarantee that Dispatcher.Run has been called yet.

What I've done to implement this properly is to use a signal to ensure the dispatcher is running before continuing to use it on the main thread.

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

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

发布评论

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

评论(2

破晓 2024-11-11 13:56:46

这是一个较短的版本,作为实用函数完成,受到你的的启发,所以我省略了评论。

private static Thread CreateDispatcherThread()
{
    using (var startedEvent = new ManualResetEventSlim()) 
    {
        var dispatcherThread = new Thread( _ => { 
            Dispatcher.CurrentDispatcher.BeginInvoke((Action)(startedEvent.Set));
            Dispatcher.Run(); } );
        dispatcherThread.Start();
        startedEvent.WaitHandle.WaitOne();
        return dispatcherThread;
    }
}   

This is a shorter version, done as a utility function, inspired by yours so I left out the comments.

private static Thread CreateDispatcherThread()
{
    using (var startedEvent = new ManualResetEventSlim()) 
    {
        var dispatcherThread = new Thread( _ => { 
            Dispatcher.CurrentDispatcher.BeginInvoke((Action)(startedEvent.Set));
            Dispatcher.Run(); } );
        dispatcherThread.Start();
        startedEvent.WaitHandle.WaitOne();
        return dispatcherThread;
    }
}   
小伙你站住 2024-11-11 13:56:46

这就是我最终所做的,我相信这是您需要做的才能正确使用调度程序。

private Thread executionThread;
private object SyncObject {get;set;}
private delegate void DispatcherMethod();

private void InitDispatcher()
{
    this.SyncObject = new object();

    // Set up the dispatcher pump.  See Dispatcher.Run on MSDN.
    this.executionThread = new Thread(StartDispatcher);

    lock (this.SyncObject)
    {
        this.executionThread.Start();
        Monitor.Wait(this.SyncObject);
    }
}   


private void StartDispatcher()
{
    DispatcherMethod method = DispatcherStarted;
    // Enqueue a started event by adding an initial method on the message pump.
    // Use BeginInvoke because the dispatcher is not actually running yet.
    // The call to Dispatcher.CurrentDispatcher handles creating the actual
    // Dispatcher instance for the thread (see MSDN - Dispatcher.FromThread
    // does not initialize the Dispatcher).
    Dispatcher.CurrentDispatcher.BeginInvoke(method);
    Dispatcher.Run();
}


private void DispatcherStarted()
{
    lock (this.SyncObject)
    {
        Monitor.Pulse(this.SyncObject);
    }
}

InitDispatcher 返回后,您可以使用

Dispatcher.FromThread(executionThread).Invoke

Dispatcher.FromThread(executionThread).BeginInvoke

来编组对调度程序线程的调用。

Here is what I ended up doing, which is what I believe you need to do in order to use the Dispatcher properly.

private Thread executionThread;
private object SyncObject {get;set;}
private delegate void DispatcherMethod();

private void InitDispatcher()
{
    this.SyncObject = new object();

    // Set up the dispatcher pump.  See Dispatcher.Run on MSDN.
    this.executionThread = new Thread(StartDispatcher);

    lock (this.SyncObject)
    {
        this.executionThread.Start();
        Monitor.Wait(this.SyncObject);
    }
}   


private void StartDispatcher()
{
    DispatcherMethod method = DispatcherStarted;
    // Enqueue a started event by adding an initial method on the message pump.
    // Use BeginInvoke because the dispatcher is not actually running yet.
    // The call to Dispatcher.CurrentDispatcher handles creating the actual
    // Dispatcher instance for the thread (see MSDN - Dispatcher.FromThread
    // does not initialize the Dispatcher).
    Dispatcher.CurrentDispatcher.BeginInvoke(method);
    Dispatcher.Run();
}


private void DispatcherStarted()
{
    lock (this.SyncObject)
    {
        Monitor.Pulse(this.SyncObject);
    }
}

After InitDispatcher returns, you can use

Dispatcher.FromThread(executionThread).Invoke

or

Dispatcher.FromThread(executionThread).BeginInvoke

to marshal calls to the dispatcher thread.

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