1 线程睡眠函数
std::this_thread::sleep_for(std::chrono::milliseconds(100));//头文件#include<chrono>,供选择的如seconds()等
不要使用睡眠函数同步线程,睡眠函数可以用于复现线程的一些行为。
线程重新调度函数:
std::this_thread::yield()
重新调度本线程,用于线程等待其它线程时而不阻塞本线程
2 条件变量std::condition_variable不允许拷贝和移动的。其基本语义和Linux的pthread_cond_t差不多。
condition_variable();
condition_variable (const condition_variable&) = delete;//没有copy constructor和move constructor
~condition_variable();//阻塞在此条件变量上的线程将被唤醒,不会有线程再对此条件变量wait
void wait (unique_lock<mutex>& lck);//等待条件发生,注意参数是unique_lock,可能发生虚假唤醒,即不是notify唤醒的
template <class Predicate>
void wait (unique_lock<mutex>& lck, Predicate pred);//pred是个函数对象返回bool,线程会在pred返回false的下阻塞,pred返回true会被唤醒,这样可以防止虚假唤醒等价于:while (!pred()) wait(lck);
template <class Rep, class Period>
cv_status wait_for (unique_lock<mutex>& lck,const chrono::duration<Rep,Period>& rel_time);//在指定rel_time时间段内等待,此期间可能被notify唤醒
template <class Rep, class Period, class Predicate>
bool wait_for (unique_lock<mutex>& lck,const chrono::duration<Rep,Period>& rel_time, Predicate pred);//为了防止虚假唤醒,加了一个pred,在rel_time内被notify且要pred返回true方可被唤醒
template <class Clock, class Duration>
cv_status wait_until (unique_lock<mutex>& lck,const chrono::time_point<Clock,Duration>& abs_time);//等待到一个指定的绝对时间点,语义和wait_for差不多
template <class Clock, class Duration, class Predicate>
bool wait_until (unique_lock<mutex>& lck,const chrono::time_point<Clock,Duration>& abs_time,Predicate pred);
void notify_one() noexcept;//若有多个线程等待此条件变量则选择一个线程唤醒,若没有线程等待则此函数什么也不做
void notify_all() noexcept;//唤醒所有阻塞在此条件变量上的线程,若没有阻塞线程此函数什么也不做
3 下面是一个使用condition_variable和queue实现的单生产者单消费者代码片段
std::mutex mut;//该mutex会被condition_variable使用
std::queue<data_chunk> data_queue;//用于在生产者线程和消费者线程间传递数据
std::condition_variable data_cond;
void data_preparation_thread()//生产者
{
while(more_data_to_prepare())
{
data_chunk const data=prepare_data();
std::lock_guard<std::mutex> lk(mut);//这里可以用lock_guard或者unique_lock
data_queue.push(data);//
data_cond.notify_one();//唤醒一个阻塞在条件变量的线程,多个阻塞线程会选择一个唤醒,若没有阻塞的线程则什么也不做
}
}
void data_processing_thread()//消费者
{
while(true)//不断消费
{
std::unique_lock<std::mutex> lk(mut);//注意condition_varibale必须使用unique_lock,因为unique_lock提供了lock和unlock操作更加灵活
data_cond.wait(lk,[]{return !data_queue.empty();});//##1##lambda表达式用于检测queue是否为空,这里可以是任意的callable object
data_chunk data=data_queue.front();
data_queue.pop();
lk.unlock();//wait返回时mutex处于locked,为了提高并发应该立即显示解锁
process(data);//处理数据
if(is_last_chunk(data))
break;
}
}
##1##处说明:
wait首先检查lambda表示是否为真(queue是否为空),若为假(queue中有数据)wait直接返回;若为假则表明条件不满足,那么wait会将mutex解锁,并使线程进入阻塞状态,这里看出为什么要用unique_lock了吧因为其提供了比lock_guard更灵活的lock和unlock操作。
当阻塞的线程被notify_one()/notify_all()唤醒时,首先对mutex上锁,并检查lambda表达式(queue是否为空的条件)。若条件满足(queue非空)则wait立即返回,mutex处于locked。若条件仍不满足(queue为空),wait将对mutex解锁,并继续阻塞线程在waiting状态。
4 测试wait前后mutex的状态
#include<iostream>
#include<thread>
#include<mutex>
#include<chrono>
#include<condition_variable>
using namespace std;
mutex m;
condition_variable cond;
int flag=0;
void producer(){
this_thread::sleep_for(chrono::seconds(1));
lock_guard<mutex> guard(m);
flag=100;
cond.notify_one();
cout<<"notify..."<<endl;
}
void customer(){
unique_lock<mutex> lk(m);
if(m.try_lock())
cout<<"mutex unlocked after unique_lock"<<endl;
else
cout<<"mutex locked after unique_lock"<<endl;//输出
while(flag==0){
cout<<"wait..."<<endl;
cond.wait(lk);
}
if(m.try_lock())
cout<<"mutex unlocked after wait"<<endl;
else
cout<<"mutex locked after wait"<<endl;//输出
cout<<"flag==100? "<<flag<<endl;
}
int main(){
thread one(producer);
thread two(customer);
one.join();
two.join();
return 0;
}
程序输出:
mutex locked after unique_lock //unique_lock(mutex&)会拥有mutex并对mutex上锁
wait...
notify...
mutex locked after wait //wait返回后mutex仍处于locked状态
flag==100? 100