这是一种在没有互斥锁的情况下使用条件变量的安全方法

时间:2013-09-19 05:58:21

标签: c++ locking mutex condition-variable

我目前的代码如下所示

void XXX::waitForUpdates()
{
    boost::unique_lock<boost::mutex> lock(mutex_agentDone);
    while(!allAgentUpdatesDone()) {
        COND_VAR_AGENT_DONE.wait(lock);
    }
}

void XXX::onAgentUpdate(YYY argums){
    Agent * target = const_cast<Agent*>(argums.GetAgent());
    boost::unique_lock<boost::mutex> lock(mutex_agentDone);
    REGISTERED_AGENTS.setDone(target,true);
        COND_VAR_AGENT_DONE.notify_all();
}

一切都很好,除非每1秒调用onAgentUpdate一百万次,我不得不担心性能和优化。

所以我想如果我将wait(lock)更改为执行timed_wait检查的allAgentUpdatesDone()版本,我可以跳过.notify() s,否则会以数百的顺序调用每秒数千!不要喘息,这是一个模拟框架:)

然后我问myseld:我需要mutex_agentDone为什么?我可以修改这两个函数:

void XXX::waitForUpdates()
{
    //this lock will become practically useless, coz there is no other 
    // mutex_agentDone being locked in any other function.
    boost::unique_lock<boost::mutex> lock(mutex_agentDone);
    while(!allAgentUpdatesDone()) {     
        COND_VAR_AGENT_DONE.timed_wait(lock,some_time_interval);
    }
}

void XXX::onAgentUpdate(YYY argums){
    Agent * target = const_cast<Agent*>(argums.GetAgent());
    REGISTERED_AGENTS.setDone(target,true)
}

问题是:这样安全吗?

谢谢

小记: 假设两个函数中的其余操作已经由它们自己的互斥锁保护(REGISTERED_AGENTS是一个类对象,在每个访问器和迭代方法中调用container并调用自己的mutex ,因此allAgentUpdatesDone()使用相同的container和REGISTERED_AGENTS迭代相同的mutex

2 个答案:

答案 0 :(得分:2)

我不太熟悉C ++ 11原子,但在Solaris上你可以使用volatile bool和membar ops这样的组合

volatile bool done = false;
void XXX::waitForUpdates()
{
//    boost::unique_lock<boost::mutex> lock(mutex_agentDone);
    while(!allAgentUpdatesDone()) {
               while ( !done )
               {
                     usleep(1000000);
                     membar_consumer();
               }
    }
}

void XXX::onAgentUpdate(YYY argums){
    Agent * target = const_cast<Agent*>(argums.GetAgent());
    //boost::unique_lock<boost::mutex> lock(mutex_agentDone);
    membar_producer();
    REGISTERED_AGENTS.setDone(target,true);
        done = true;
}

由于 Niraj Rathi

答案 1 :(得分:2)

你可以这样做:

void XXX::waitForUpdates()
{
    boost::unique_lock<boost::mutex> lock(mutex_agentDone);
    while(!allAgentUpdatesDone()) {
        ++waiters;
        COND_VAR_AGENT_DONE.wait(lock);
        --waiters;
    }
}

void XXX::onAgentUpdate(YYY argums){
    Agent * target = const_cast<Agent*>(argums.GetAgent());
    boost::unique_lock<boost::mutex> lock(mutex_agentDone);
    REGISTERED_AGENTS.setDone(target,true);
    if (waiters != 0)
        COND_VAR_AGENT_DONE.notify_all();
}

互斥锁保护waiters计数。确保将其设置为零才能启动。

你会期望条件变量已经有类似的东西,但只是调用notify_all的开销可能很大。

这假定大部分时间没有服务员。如果问题是大部分时间allAgentUpdatesDone都会返回false,那么除非所有更新完成,否则请不要致电notify_all