Multithreading 如何缓解UI线程和Dispatchermer.Tick事件之间的争用情况?

Multithreading 如何缓解UI线程和Dispatchermer.Tick事件之间的争用情况?,multithreading,uwp,task,uwp-xaml,Multithreading,Uwp,Task,Uwp Xaml,我相信我在这个代码示例中有一个竞争条件,但我不确定如何缓解它 我的场景是XAsync()始终在UI线程上运行。在XAsync()中,我设置m_importantMemberVariable,然后启动计时器;计时器启动前有1秒的延迟 我关心的是计时器的tick事件调用m_importantMemberVariable上的方法。但是,在启动计时器和触发勾号之间的1秒间隔内,可以再次调用XAsync()并覆盖m_importantMemberVariable 代码示例: task<void>

我相信我在这个代码示例中有一个竞争条件,但我不确定如何缓解它

我的场景是XAsync()始终在UI线程上运行。在XAsync()中,我设置m_importantMemberVariable,然后启动计时器;计时器启动前有1秒的延迟

我关心的是计时器的tick事件调用m_importantMemberVariable上的方法。但是,在启动计时器和触发勾号之间的1秒间隔内,可以再次调用XAsync()并覆盖m_importantMemberVariable

代码示例:

task<void> BobViewModel::XAsync()
{
    return create_task(CreateSomethingAsync())
        .then([this](SomethingAsync^ aThing)
    {
        this->m_importantMemberVariable = aThing;
        OnPropertyChanged("ImportantMemberVariable");

        // Timer has 1 second delay.
        this->m_myDispatcherTimer->Start();
    }, task_continuation_context::use_current())
        .then([activity](task<void> result)
    {
        // more continuations...
    });
}

void BobViewModel::OnTimerTick(Object^, Object^)
{
    // Stopping the timer and detaching the event handler
    // so timer only fires once.
    m_myDispatcherTimer->Stop();
    m_myDispatcherTimer->Tick -= m_impressionTimerToken;
    m_myDispatcherTimer = { 0 };

    // * Possible race condition *
    m_importantMemberVariable->DoImportantThing();
}
任务BobViewModel::XAsync()
{
返回创建任务(CreateSomethingAsync())
.然后([这个](一些同步的东西)
{
此->m_importantMemberVariable=aThing;
OnPropertyChanged(“ImportantMemberVariable”);
//定时器有1秒的延迟。
此->m_MyDispatchermer->开始();
},任务\继续\上下文::使用\当前()
。然后([活动](任务结果)
{
//更多的延续。。。
});
}
void BobViewModel::OnTimerTick(对象^,对象^)
{
//停止计时器并分离事件处理程序
//所以定时器只触发一次。
m_MyDispatcher->Stop();
m_MyDispatchermer->Tick-=m_impressionTimerToken;
m_myDispatcher={0};
//*可能的竞争条件*
m_importantMemberVariable->DoImportantThing();
}
问题:假设我对比赛条件的看法是正确的,有没有办法缓解这种情况

我的理解是,勾号事件会在UI线程上触发,因此同步原语不会有帮助(因为UI线程已经有访问权限)。

的答案建议使用compare_exchange___strong和std::atomic来确保一次只有一个线程执行一个函数。对于这个问题,这种方法的问题是:
1.Dispatchermer Tick事件在任务延续块之外激发,并且可以在延续完成后激发。
2.这个问题的一个限制是计时器只能触发一次

一些替代解决方案包括:

  • 使用compare\u exchange\u strong,但将Dispatchermer替换为create\u delayed\u任务
  • 假设工作不必发生在UI线程上,您可以使用来延迟任务延续中的工作

    task<void>
    BobViewModel::UseImportantVariableAsync(
        Object^ importantVariable
    )
    {
        return create_delayed_task(
            std::chrono::milliseconds(1000),
            [importantVariable]()
        {
            importantMemberVariable->DoImportantThing();
        });
    }
    
  • 将lambda用于Dispatcher的Tick事件,并从问题的示例中捕获“aThing”(而不是引用处理程序中的成员变量)。要仅触发计时器一次,请在块内分配DispathcerTimer.Tick处理程序,以便只有第一个调用方可以执行此操作

  • 您的所有操作都在UI线程上,因此它们已经为您序列化(同步)。一个简单的标志就足够了:

    bool m_busy; // set to false in constructor
    
    task<void> BobViewModel::XAsync()
    {
        if (m_busy)
          return;
    
        m_busy = true;
        // the rest of your code...
    }
    
    void BobViewModel::OnTimerTick(Object^, Object^)
    {
        m_busy = false;
        // the rest of your code...
    }
    
    bool m_busy;//在构造函数中设置为false
    任务BobViewModel::XAsync()
    {
    如果(忙)
    回来
    m_busy=真;
    //剩下的代码。。。
    }
    void BobViewModel::OnTimerTick(对象^,对象^)
    {
    m_busy=错误;
    //剩下的代码。。。
    }
    

    只要确保处理任何异常,以便在出现严重错误时将
    mu busy
    设置回
    false

    没有竞争。唯一可能出错的是在更新m_importantMemberVariable后不到1秒就会调用DoImportantThing()。代码在调用Start()之前忘记停止()计时器。@HansPassant-如果XAsync的第二个调用方在计时器启动后(但在触发tick事件之前)将m_importantMemberVariable设为null,那么如何不产生null引用异常?你是说那不可能发生吗?如果是,原因是什么?如果您遵循建议,那么您停止了计时器。当变量为null时,为什么还要再次调用Start()?这似乎是重复了您已经问过两次的问题,我已经回答了。@PeterTorr MSFT-不同之处在于,这个问题将Dispatcher加入到混合中,因此它不限于任务的继续。
    bool m_busy; // set to false in constructor
    
    task<void> BobViewModel::XAsync()
    {
        if (m_busy)
          return;
    
        m_busy = true;
        // the rest of your code...
    }
    
    void BobViewModel::OnTimerTick(Object^, Object^)
    {
        m_busy = false;
        // the rest of your code...
    }