C++ 并发编程实战: 第4章 线程同步

361 阅读6分钟

第4章 线程同步

本章内容

  • 条件变量
  • future 等待一次性事件的发生
  • async()
  • promise<>
  • package_task<>
  • shared_future
  • 时间类

1. 条件变量

概念

条件变量是特殊的信号量, wait 相当于 P 操作, notice 相当于 V 操作

条件变量有两种:

  • condition_variable 只与 mutex 配合使用
  • condition_variable_any 可与任何互斥配合使用

源码

condition_variable

class condition_variable 
{ 
public:
    condition_variable() 
    {
        _Cnd_init_in_situ(mycnd());
    }
​
    ~condition_variable() 
    {
        _Cnd_destroy_in_situ(mycnd());
    }
​
    condition_variable(const condition_variable&) = delete;
    condition_variable& operator=(const condition_variable&) = delete;
​
    // V 操作
    void notify_one() 
    {   
        _Cnd_signal(mycnd());
    }
​
    void notify_all() 
    {   
        _Cnd_broadcast(mycnd());
    }
​
    // P 操作
    void wait(unique_lock<mutex>& lck) 
    {   
        _Cnd_wait(mycnd(), lck.mutex()->mymtx());
    }
​
    template <class Func>
    void wait(unique_lock<mutex>& lck, Func func) 
    {   // 除了要有 V 操作(signal), 对应的条件也得满足
        while (!func()) 
        {
            wait(lck);
        }
    }
​
​
    // 时间段
    template <class _Rep, class _Period>
    int wait_for(unique_lock<mutex>& lck, const chrono::duration<_Rep, _Period>& rel_time) {}
​
    template <class _Rep, class _Period, class Func>
    bool wait_for(unique_lock<mutex>& lck, const chrono::duration<_Rep, _Period>& rel_time, Func func) {}
​
​
    // 时间点
    template <class _Clock, class _Duration>
    int wait_until(unique_lock<mutex>& lck, const chrono::time_point<_Clock, _Duration>& abs_time) {}
​
    template <class _Clock, class _Duration, class Func>
    bool wait_until(unique_lock<mutex>& lck, const chrono::time_point<_Clock, _Duration>& abs_time, Func func) {}
​
private:
    struct cnd_storage;
​
    void* mycnd() 
    {  
        return reinterpret_cast<void*>(&cnd_storage);
    }
};

condition_variable_any

class condition_variable_any 
{  
public:
    condition_variable_any() : myptr{make_shared<mutex>()} 
    {
        _Cnd_init_in_situ(mycnd());
    }
​
    ~condition_variable_any() 
    {
        _Cnd_destroy_in_situ(mycnd());
    }
​
    condition_variable_any(const condition_variable_any&) = delete;
    condition_variable_any& operator=(const condition_variable_any&) = delete;
​
    // V 操作
    void notify_one() 
    { 
        lock_guard<mutex> guard{*myptr};
        _Cnd_signal(mycnd());
    }
​
    void notify_all() 
    { 
        lock_guard<mutex> guard{*myptr};
        _Cnd_broadcast(mycnd());
    }
​
    // P 操作
    template <class Lock>
    void wait(Lock& lck) 
    { 
        const shared_ptr<mutex> p = myptr; // for immunity to *this destruction
​
        unique_lock<mutex> guard{*p};
        lck.unlock();
        _Cnd_wait(mycnd(), p->mymtx());     // 真正的 P 操作
        guard.unlock();
        lck.lock();                         // 生产者消费者模型, 临界区的互斥访问加锁
    } 
​
    template <class Lock, class Func>
    void wait(Lock& lck, Func func)
    {
        while (!static_cast<bool>(func())) 
        {
            wait(lck);
        }
    }
​
    template <class Lock, class _Clock, class _Duration>
    int wait_until(Lock& lck, const chrono::time_point<_Clock, _Duration>& abs_time) {}
    template <class Lock, class _Clock, class _Duration, class Func>
    bool wait_until(Lock& lck, const chrono::time_point<_Clock, _Duration>& abs_time, Func func) {}
​
    template <class Lock, class _Rep, class _Period>
    int wait_for(Lock& lck, const chrono::duration<_Rep, _Period>& rel_time) {}
    template <class Lock, class _Rep, class _Period, class Func>
    bool wait_for(Lock& lck, const chrono::duration<_Rep, _Period>& rel_time, Func func) {}
​
private:
    shared_ptr<mutex> myptr;
​
    struct cnd_storage;
​
    void* mycnd() 
    { 
        return reinterpret_cast<void*>(&cnd_storage);
    }
};

成员函数

wait

相当于 P 操作

void wait(unique_lock<mutex>& l)
{
    l.unlock();
    P();
    l.lock();
}
​
void wait(unique_lock<mutex>& l, Func func)
{
    while(!func())
    {
        wait(l);
    }
}

notify_one 和 notify_all

相当于 V 操作, 唤醒等待队列的线程

如果等待队列为空, 就结束, 有可能导致错过 wait

即:

先进行 notify_one 操作, 发现等待队列为空, 就结束

再进行 wait 操作把当前线程加入等待队列等待唤醒, 然后永远等不到 notify_one

void notify_one() 
{ 
    _Cnd_signal(mycnd());   // 唤醒一个
}
​
void notify_all() 
{ 
    _Cnd_broadcast(mycnd());// 唤醒所有
}

使用

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>using namespace std;
​
mutex m;
condition_variable cnd;
​
int x;
​
void A()
{
    x = 1;
​
    cnd.notify_one();       // V 操作
}
​
void B()
{
    unique_lock<mutex> l(m);// 加锁
    cnd.wait(l, [] {return x == 1; });          // 先进行 P 操作, 再解锁加锁
    l.unlock();             // 解锁
​
    cout << x << endl;
}
​
int main()
{
    thread t2(B);
    thread t1(A);
​
    t2.join();
    t1.join();
}

类比信号量实现线程同步

A:
{
    ...
    V(s);       // cnd.notify_one();
}
​
B:
{
    P(s);       // cnd.wait(l);
    ...
}

唤醒丢失

正常顺序: A B

B 等待 A 的信号

  • 若 A 先执行, 发出了信号, 但 B 还没开始, 错过了 A 的信号, B 就永远等不到 A 的信号了
  • 若 B 先执行, 由于需要等待 A 的信号, 所以加入等待队列, 然后 A 执行, 发出信号, 唤醒 B, 正常执行
A:
{
    ...
    notify_one();   // 发出信号, 而不是设置状态        
}
​
B:
{
    wait();         // 等待信号
    ...
}
​
唤醒丢失:   
notify_one();       // 发出信号
wait();             // 加入队列后等待信号

2. future

概念

future 对象跟一次性事件关联, 事件发生后, 存储结果

future 可以存储异常

  • furure 一个事件关联一个 future 实例

    阅后即焚

  • shared_future 一个事件关联多个 shared_future 实例

    shared_future 对象可以被拷贝, 多次 get 获取结果

模板类, 模板参数表示返回值的类型

成员函数

get() 阻塞, 直至获取到关联事件的结果

get() 只能调用一次

链式调用

源码

template <class T>
class future : public _State_manager<T> 
{
    using base = _State_manager<T>;
​
public:
    future() {}
    future(const base& state, _Nil) : base(state, true) {}
​
    ~future() {}
​
    // 移动操作
    future(future&& other) : base(move(other), true) {}
    future& operator=(future&& right) 
    {
        base::operator=(move(right));
        return *this;
    }
​
    T get() 
    {
        // 阻塞, 直到返回结果或抛出异常
        future _Local{move(*this)};
        return move(_Local._Get_value());
    }
​
    // 显式转换为 shared_future
    shared_future<T> share() 
    {
        return shared_future<T>(move(*this));
    }
​
    future(const future&) = delete;
    future& operator=(const future&) = delete;
};

3. 获得 future 的方式

async()

函数结果与 future 对象关联

源码

template <class Func, class... Args>
future async(Func&& func, Args&&... args) 
{
    return async(launch::async | launch::deferred, 
                 forward<Func>(func), 
                 forward<Args>(args)...);
}

参数

launch::async 创建新线程执行函数

launch::deferred 延迟调用, future 调用 wait() 或 get() 时, 才执行函数, 没有创建新线程

使用

int fun(int)
{
    return 0;
}
​
A:
{
    future<int> f = async(fun, 0);  // V 操作
    cout << f.get() << endl;        // P 操作
}

promise<>

与 future 对象关联

成员函数

  • set_value 设置值
  • set_exception 设置异常
  • get_future 获取与 promise 关联的 future

使用

promise<int> p;
future<int> f = p.get_future();     // f 与 p 关联void A(promise<int>& p)
{
    p.set_value(1);                 // V 操作
}
​
void B(future<int>& f)
{
    cout << f.get() << endl;        // P 操作
}

源码

template <class T>
class promise 
{ 
public:
    promise() : myPromise(new _Associated_state<T>) {}
​
    // 移动操作
    promise(promise&& other) : myPromise(move(other.myPromise)) {}
    promise& operator=(promise&& other) 
    {
        promise(move(other)).swap(*this);
        return *this;
    }
​
    ~promise() 
    {
        if (myPromise._Is_valid() && !myPromise._Is_ready() && !myPromise._Is_ready_at_thread_exit()) 
        {
            // exception if destroyed before function object returns
            future_error _Fut(make_error_code(future_errc::broken_promise));
            myPromise._Get_state()._Set_exceptiargson(make_exception_ptr(_Fut), false);
        }
    }
​
    // 返回与 promise 关联的 future
    future<T> get_future() 
    {
        return future<T>(myPromise._Get_state_for_future(), _Nil{});
    }
​
    // 左值
    void set_value(const T& val) 
    {
        myPromise._Get_state_for_set()._Set_value(val, false);
    }
    void set_value_at_thread_exit(const T& val) {
        myPromise._Get_state_for_set()._Set_value(val, true);
    }
​
    // 右值
    void set_value(T&& val) 
    {
        myPromise._Get_state_for_set()._Set_value(forward<T>(val), false);
    }
    void set_value_at_thread_exit(T&& val) 
    {
        myPromise._Get_state_for_set()._Set_value(forward<T>(val), true);
    }
​
    // 异常
    void set_exception(exception_ptr exc) 
    {
        myPromise._Get_state_for_set()._Set_exception(exc, false);
    }
    void set_exception_at_thread_exit(exception_ptr exc) 
    {
        myPromise._Get_state_for_set()._Set_exception(exc, true);
    }
​
    promise(const promise&) = delete;
    promise& operator=(const promise&) = delete;
​
    void swap(promise& other) 
    {
        myPromise._Swap(other.myPromise);
    }
​
private:
    _Promise<T> myPromise;
};

packaged_task<>

函数与 future 关联

成员函数

  • get_future 获取与函数关联的 future
  • operator 调用关联的函数
  • valid 是否有效, 即是否为可调用对象
  • reset 重置为初始状态: 需要重新 get_future 和执行

使用

int fun(int a, int b)
{
    return a + b;
}
​
A:
{
    packaged_task<int(int, int)> p(fun);
    future<int> f = p.get_future();
​
    thread t(move(p), 1, 2);    // V 操作
    cout << f.get() << endl;    // P 操作
​
    t.join();
}

源码

template <class Ret, class... Args>
class packaged_task<Ret(Args...)> 
{
public:
    packaged_task() : myPromise(0) {}
​
    template <class _Fty2, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, packaged_task>, int> = 0>
    explicit packaged_task(_Fty2&& _Fnarg) : myPromise(new _MyStateType(forward<_Fty2>(_Fnarg))) {}
​
    packaged_task(packaged_task&& other) : myPromise(move(other.myPromise)) {}
    packaged_task& operator=(packaged_task&& other) 
    {
        myPromise = move(other.myPromise);
        return *this;
    }
​
    ~packaged_task() 
    {
        myPromise._Get_state()._Abandon();
    }
​
    void swap(packaged_task& other) 
    {
        swap(myPromise, other.myPromise);
    }
​
    bool valid() const 
    {
        return myPromise._Is_valid();
    }
​
    future<Ret> get_future() 
    {
        return future<Ret>(myPromise._Get_state_for_future(), _Nil{});
    }
​
    void operator()(Args... _Args) {}
​
    void reset() {}
​
    packaged_task(const packaged_task&) = delete;
    packaged_task& operator=(const packaged_task&) = delete;
​
private:
    promise<Ret> myPromise;
};

shared_future

一个事件关联多个 shared_future 实例

  • shared_future 对象可以被拷贝, 多次 get 获取结果
int fun(int)
{
    return 0;
}
​
A:
{
    shared_future<int> f = async(fun, 0);   // 隐式类型转换
    cout << f.get() << endl;        
    cout << f.get() << endl;        
}

4. 时间类

时间段

template<class Rep,class Period = std::ratio<1>> class duration;

  • Rep 数据类型

  • Period 单位类型: 1 单位的秒数

    std::ratio<a,b> 表示 a / b

成员函数

  • count 时间数值本身

使用

std::chrono::duration<short, std::ratio<60,1>>  
short 存储, 一单位 = 60s
​
std::chrono::duration<double,std::ratio<1,1000>>
double 存储, 一单位 = 1/1000s

时间点

template<class Clock,class Duration = typename Clock::duration> class time_point;

  • Clock 时钟类型
  • Duration 时间段类型

使用

time_point <system_clock,duration<int>> t(duration<int>(1));

时钟类

每个 clock 类都有确定的 duration, Rep, Period, time_point 类型

  • std::chrono::steady_clock 恒稳时钟
  • std::chrono::system_clock 系统时钟
  • std::chrono::high_resolution_clock 高精度时钟

成员函数

  • now 返回当前时间点

使用

auto t = chrono::system_clock::now();
...
auto t2 = chrono::system_clock::now();
​
cout << (t2 - t).count() << endl;

总结

线程同步的方式本质上都是 P/V 操作

  • 条件变量

    • V : notify_one 和 notify_all
    • P : wait
  • future

    • V : async, promise.set_value, thread t(packaged_task<>, args)
    • P : future.get
  • shared_future

    • V : 类似 future
    • P : shared_future.get
  • 时间类

    • 时间段
    • 时间点
    • 时钟