第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
-
时间类
- 时间段
- 时间点
- 时钟