"data race"(不是真的)在通知条件变量并解锁关联的互斥锁后

"data race" (not really) after notifying condition variable and unlocking associated mutex

本文关键字:关联 解锁 变量 通知 race data 真的 条件      更新时间:2023-10-16

我的问题如下所示。线程 II 可能会错过有关std::condition_variable的通知;线程 III 可以在它之前获取锁定并更改条件。

/*  
     Thread I          Thread II                 Thread III
_____________________________________________________________
| lock M        | wait for notify         | wait for M    |   |
| cond = stateA |                         |               |   |
| notify        | unblock                 |               |   |
| unlock M      | wait for M              | lock M        |   |
|               |                         | cond = stateB |   |
|               | lock M                  | unblock M     |   |
|               | check if cond == stateA |               |   |   
|               |      ...                |               |   / t
                                                              *       
    */
#include <iostream>
#include <condition_variable>
#include <chrono>
#include <thread>
#include <limits>
#include <mutex>
int main() 
{
    using namespace std::chrono ;
    std::mutex mtx ;
    std::condition_variable cv ;
    enum EState
    {
        A , B
    } state = B ; // mtx
    // possible workaround
    using count_t = unsigned long long ;
    count_t set_A_state_count = 0 ; // mtx
    // 18,446,744,073,709,551,615 - number, that may cause missing ;
    // ( if Thread III function would get executed exactly this number of times
    // before Thread II acquire the mutex )   
    // believe it is not relevant for present days.
    auto ThreadI = [ &set_A_state_count , &cv ,
                     &mtx , &state ] () 
    { 
        std::lock_guard< std::mutex > lock { mtx } ;
        state = A  ;
        ++ set_A_state_count ;
        cv.notify_one() ;
    } ;
    auto ThreadIII = [ &cv , &mtx , &state ] () 
    { 
        std::lock_guard< std::mutex > lock { mtx } ;
        state = B ;
    } ;

    std::unique_lock< std::mutex > lock { mtx } ;
    std::thread thI ( ThreadI ) , thIII ( ThreadIII ) ;
    const auto saved_count = set_A_state_count ;
    if ( state != A ) {
       while( saved_count == set_A_state_count ) { // pred ()
        // releasing and waiting 
           cv.wait( lock ) ;
        // acquiring - place where ThreadIII can outrun main thread ( ThreadII on the inlustration )
       }
    }
    count_t times = ( saved_count < set_A_state_count ) ?
                                   set_A_state_count - saved_count 
                                 : std::numeric_limits< count_t >::max() - 
                                      set_A_state_count + saved_count ;
    std::cout << "state was changed to A " << times << " times." << std::flush ; 
    thI.join() ;
    thIII.join() ;
    return 0;
}

有什么办法可以解决这个问题吗?

(应用程序(。考虑一些带有"wait( state ("、"start"和"cancel"方法的"alarm"类。它有关联的线程,即"服务员"线程。可以在单个对象上调用所有方法。虽然取消和启动可以与其他互斥锁同步,但由于明显的原因,无法等待。可以通过在每次等待之前简单地存储一些 ulong 计数器的状态,然后比较存储和当前 - 如果它们不同(通过启动或取消递增(,则状态切换,发生通知。

线程 II 可能会错过有关std::condition_variable的通知;

如果在发出通知时没有线程等待条件变量通知,则可能会错过条件变量通知。代码必须等待状态的更改。条件变量通知是状态可能已更改且必须重新评估的提示。

您有 2 个线程竞相将state更改为 A 并相应地B。它们之间没有排序约束,因此state可以是A的,也可以是B的。在后一种情况下,等待state == A的线程永远阻塞。