3

我有一个带有管理器(树类)的多线程应用程序(使用 std::thread),它在不同的子树(嵌入式结构子树)上并行执行一些代码。基本思想是 SubTree 的每个实例都有一个存储对象的双端队列。如果双端队列为空,则线程等待直到在双端队列中插入新元素或达到终止条件。一个子树可以生成对象并将它们推送到另一个子树的双端队列中。为方便起见,我所有的 std::mutex、std::locks 和 std::variable_condition 都存储在一个名为“locks”的结构中。

Tree 类创建了一些运行以下方法的线程(第一次尝试):

void Tree::launch(SubTree & st, Locks & locks )
{
/* some code */

std::lock_guard<std::mutex> deque_lock(locks.deque_mutex_[st.id_]) ; // lock the access to the deque of subtree st
if (st.deque_.empty())  // check that the deque is still empty
{
    // some threads are still running, wait for them to terminate

    std::unique_lock<std::mutex> wait_lock(locks.restart_mutex_[st.id_]) ;
    locks.restart_condition_[st.id_].wait(wait_lock) ;   
}

/* some code */
}

问题是“deque_lock”在线程等待时仍然被锁定。因此,并发对象不能在当前线程的双端队列中添加任何对象。

所以我把 lock_guard 变成了一个 unique_lock 并手动管理锁定/解锁:

void launch(SubTree & st, Locks & locks )
{
/* some code */

std::unique_lock<std::mutex> deque_lock(locks.deque_mutex_[st.id_]) ; // lock the access to the deque of subtree st
if (st.deque_.empty())          // check that the deque is still empty
{
    deque_lock.unlock() ; // unlock the access to the deque to enable the other threads to add objects

    // DATA RACE : nothing must happen to the unprotected deque here !!!!!!

    // some threads are still running, wait for them to terminate

    std::unique_lock<std::mutex> wait_lock(locks.restart_mutex_[st.id_]) ;
    locks.restart_condition_[st.id_].wait(wait_lock) ;   
}

/* some code */
} 

现在的问题是存在数据竞争,我想确保在“deque_lock.unlock()”指令之后直接执行“等待”指令。有人知道用标准库创建如此关键的指令序列的方法吗?

提前致谢。

4

1 回答 1

-1

最好不要假设,当您从等待条件变量返回时,您等待的条件已满足

我宁愿把这段代码写成:

std::unique_lock<std::mutex> deque_lock(locks.deque_mutex_[st.id_]);
while(st.deque_.empty())
{
    deque_lock.unlock();
    std::unique_lock<std::mutex> wait_lock(locks.restart_mutex_[st.id_]);
    locks.restart_condition_[st.id_].wait(wait_lock);   
    deque_lock.lock();
}

此代码保证,在此之后您的队列不为空。std::condition_variable::wait甚至可以带一个谓词来模拟这种行为之王(但是,由于队列锁定,它不能在这里使用)。

于 2012-06-11T22:40:03.047 回答