条件变量底层实现原理

条件变量底层实现原理剖析

在并发编程中,条件变量用来控制线程之间的同步,总的来说条件变量相关的操作主要有两个——wait和notify,在写博客时我有这样一些疑问:

1.wait操作是如何实现的,它是如何让线程进入睡眠的

2.notiy操作是如何做到通知正在等待的线程的,并且如何做到notify单个线程和notify所有线程的,如果是notify所有线程,那么这些被唤醒的线程可以并行执行吗(多核情况下)

3.为什么在调用wait操作时,需要传入mutex互斥锁,而notify时却不用。

4.c++中的条件变量为什么要配合unique_lock<mutex>锁而不是std::mutex使用或者lock_guard<mutex>

本文从以下几个方面对条件变量进行深入的理解

1.C++中的condition_variable实现原理

2.linux pthread库中的pthread condition variable实现原理

3.从操作系统源码的角度理解条件变量是如何实现的

4.有哪些错误的条件变量实现方式

条件变量使用的例子

例1

参考:C++ std::condition_variable 条件变量类探索:解锁条件变量的底层原理_std 条件变量-优快云博客

include<iostream>// std::coutinclude<thread>// std::threadinclude<mutex>// std::mutex, std::unique_lockinclude<condition_variable>// std::condition_variableusingnamespace std;
 
mutex mtx;                          // 互斥量
condition_variable cv;              // 条件变量bool ready = false;                 // 标志量voidprint_id(int id){unique_lock<mutex> lck(mtx);    // 上锁while (!ready) {
        cv.wait(lck);               // 线程等待直到被唤醒(释放锁 + 等待,唤醒,在函数返回之前重新上锁)
    }
    cout << "thread " << id << '\n';
}
 
voidgo(){unique_lock<mutex> lck(mtx);    // 上锁
    ready = true;
    cv.notify_all();                // 唤醒所有正在等待(挂起)的线程(在这里面要释放锁,为了在wait函数返回之前能成功的重新上锁)
}
 
intmain(){
    thread threads[10];for (int i = 0; i<10; ++i) {
        threads[i] = thread(print_id, i);
    }
    cout << "10 threads ready to race...\n";go();
 for (auto& th : threads) {
        th.join();
    }
 return0;
}

从这个例子可以看出,在调用notify和wait时,都需要进行加锁操作,所以mutex互斥锁锁住的其实是ready这个变量。到这里第二个问题中的“被唤醒的线程能否并行执行”也就有答案了,答案是不能,因为每个线程是需要获得互斥锁unique_lock<mutex>的。

对于第四个问题,答案是出于安全考虑,主要是防止程序员忘记对std::mutex进行unlock操作,特别是在一些异常处理中。至于为什么不用lock_guard[std::mutex](std::mutex),这个在后面介绍wait底层实现时说明

C++中的condition_variable实现原理

参考:C++ std::condition_variable 条件变量类探索:解锁条件变量的底层原理_std 条件变量-优快云博客

c++ 11引入了条件变量,在gcc中的实现位于libstdc++-v3/include/std/condition_variable路径下(为了方面阅读,本文对源码略有修改)

class condition_variable
  {
    __condvar _M_cond;

  public:
    typedef __gthread_cond_t *native_handle_type;

    condition_variable() noexcept;
    ~condition_variable() noexcept;

    condition_variable(const condition_variable &) = delete;
    condition_variable &operator=(const condition_variable &) = delete;

    void
    notify_one() noexcept;

    void
    notify_all() noexcept;
    void
    wait(unique_lock<mutex> &__lock) {
        _M_cond.wait(*__lock.mutex());
    }

    template <typename _Predicate>
    void
    wait(unique_lock<mutex> &__lock, _Predicate __p)
    {
      while (!__p())
        wait(__lock);
    }
  }

可以看到wait/notify操作实际上是调用__condvar的接口,可以看到wait操作中获取了互斥量,从而在底层调用互斥量的lock/unlock接口,而lock_guard属于RAII 类,在构造函数中加锁,在析构函数中解锁,除此之外无法对其管理的互斥量进行lock/unlock接口。这里解答了第四个问题,由于wait底层操作中需要对互斥量进行lock/unlock操作,lock_guard并不满足这样的需求。

__condvar实际上是对glibc中pthread condition variable的一层包装,如下:

class __condvar
  {
  public:
   ...
    void
    wait(mutex &__m)
    {
      int __e __attribute__((__unused__)) = __gthread_cond_wait(&_M_cond, __m.native_handle());
      __glibcxx_assert(__e == 0);
    }

    void
    wait_until(mutex &__m, timespec &__abs_time)
    {
      __gthread_cond_timedwait(&_M_cond, __m.native_handle(), &__abs_time);
    }

    ...
    void
    notify_one() noexcept
    {
      int __e __attribute__((__unused__)) = __gthread_cond_signal(&_M_cond);
      __glibcxx_assert(__e == 0);
    }

    void
    notify_all() noexcept
    {
      int __e __attribute__((__unused__)) = __gthread_cond_broadcast(&_M_cond);
      __glibcxx_assert(__e == 0);
    }

  protected:
#ifdef __GTHREAD_COND_INIT
    pthread_cond_t _M_cond = __GTHREAD_COND_INIT;
#else
    pthread_cond_t _M_cond;
#endif
  };

可以看到,wait/notify操作底层主要是调用glibc的pthread condition variable接口。

glibc中的pthread condition variable实现原理

在网上找了一篇博客,从源码角度分析了条件变量的实现原理,细节暂时看不懂,以后再看吧深入了解glibc的条件变量_牛客网

看了这篇博客可以解答第三个问题,对于wait操作,其底层实现是需要对互斥量进行lock/unlock操作的,因此需要传入unique_lock

TODO

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值