创建线程
void* fun()//线程函数
{
//...
}
thread t1(fun);
t1.join();
//t1.detach();
join() 是两个线程交汇的意思,工作线程和主线程在此处交汇,jion() 之后的主线程会阻塞直到工作线程运行结束。
detach() 使线程函数脱离线程对象,即当线程对象销毁了线程函数依然可能运行。通常不推荐这么做。
互斥锁的使用
std::mutex;
std::lock_guard<T>//模板类,RAII封装,基于作用域
std::unique_lock<T>//模板类
mutex 需要手动加锁、解锁,如果中间抛出异常可能会无法顺利解锁,造成死锁
std::mutex mymutex;
void* threadFunction()
{
mymutex.lock();
//计算过程
//如果此时抛出异常可能无法完成解锁,找出死锁
mymutex.unlock();
}
于是有了RAII封装的lock_guard、unique_lock等模板类
std::mutex mymutex;
void* threadFunction()
{
std::lock_guard<std::mutex> lock(mymutex);//该作用域加锁
//计算过程
//如果此时抛出异常可能无法完成解锁,依然可以解锁
}
互斥锁是对共享资源的独占,同一时刻只能有一个线程占有该共享资源
条件变量
C++ 11中有std::condition_variable ,可以和互斥锁一起使用,比如在生产者消费者问题中,当共有资源为空时,消费者线程应处于等待状态,当生产者线程生产出来商品时,应该通知消费者线程将其唤醒。
int num=0;//共享资源
std::mutex mymutex;
std::condition_variable mycv;
void* consumer()
{
while(true)
{
unique_lock<mutex> lock(mymutex);
while(num==0)
{
mycv.wait(lock);//消费者线程进入等待状态
}
--num;
}
}
void* producer()
{
while(true)
{
lock_guard<mutex> lock(mymutex);
++num;
mycv.notify_noe();//通知另一个线程,将处于等待状态的消费者线程唤醒
}
}
信号量
C++中目前没有信号量,操作系统中有提供信号量的API
信号量实现线程同步是通过信号量的的资源数来实现的,互斥锁其实可以看作是一种特殊的信号量,互斥锁可以看作是信号量为1的特殊情况。
信号量就像是现在有若干把钥匙,只有当前还有剩余的钥匙,人才能进入房间,钥匙的数量就是信号量的值,线程就好比人,人进去房间就必须要获得钥匙,而线程要正常运行就必须获得信号量。
Linux中常用的信号量API如下:
#include <semaphore.h>
int sem_init(sem_t* sem, int pshared, unsigned int value);
int sem_destroy(sem_t* sem);
int sem_post(sem_t* sem);
int sem_wait(sem_t* sem);
int sem_trywait(sem_t* sem);
int sem_timedwait(sem_t* sem, const struct timespec* abs_timeout);
信号量的使用
//首先声明信号量
sem_t mysemaphore;
//信号量初始化
//第一个参数是要初始化的信号量,第二个参数通常设为0,
//第三个参数是信号量初始的值,
sem_init(&mysemaphore,0,0);
sem_post(&mysemaphore);,释放信号量的资源,信号量的资源计数+1,如果信号量由0变为1则处于等待状态的线程将会被唤醒
sem_wait(&mysemaphore);
若信号量的资源计数值为0,则该线程将处于阻塞状态;
如果信号量的资源计值大于0则该线程将会被唤醒,同时将计数值-1;
函数调用成功将会返回0
如果有多个线程处于等待中,同时线程资源数为1则只有一个线程会被唤醒。