我相信此代码示例中存在竞争条件,但我不确定如何减轻它。
我的场景是XAsync()总是在UI线程上运行。在XAsync()中,我设置m_importantMemberVariable,然后启动一个计时器;在计时器开火之前有1秒的延迟。
我担心的是计时器的tick事件会调用m_importantMemberVariable上的方法。但是,在启动计时器和Tick发射之间的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();
}
问题:假设我对比赛情况有所了解,有没有办法减轻它?
我的理解是会在UI线程上触发tick事件,因此同步原语不会有帮助(因为UI线程已经具有访问权限)。
答案 0 :(得分:1)
您的所有操作都在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...
}
请确保您处理任何异常,以便在出现严重错误时将m_busy
设置回false
。
答案 1 :(得分:0)
this question的答案建议将compare_exchange_strong与std :: atomic一起使用,以确保一次只有一个线程执行一个函数。对于这个问题,这种方法的问题是:
1. DispatcherTimer Tick事件在任务延续块之外触发,并且可以在继续完成后触发
这个问题的一个限制是计时器只能触发一次。
一些替代解决方案是:
假设工作不必在UI线程上发生,您可以使用create_delayed_task来延迟任务延续中的工作。
task<void>
BobViewModel::UseImportantVariableAsync(
Object^ importantVariable
)
{
return create_delayed_task(
std::chrono::milliseconds(1000),
[importantVariable]()
{
importantMemberVariable->DoImportantThing();
});
}
然后,从任务继续,只需:
return UseImportantVariableAsync(m_importantMemberVariable);