线程是程序中完成一个独立任务的完整执行序列,即一个可调度的实体。
Linux线程库:
Linux上两个最有名的线程库是 LinuxThreads 和NPTL。
用户可以使用如下命令来查看当前系统上所使用的的线程库
getconf GNU_LIBPTHREAD_VERSION
(我查了下自己的,发现我系统上使用的线程库是NPTL2.12 )
多线程程序要考虑同步问题。有问题当然就有解决的办法!!
下面我们讨论3种专门用于线程同步的机制:信号量、互斥量和条件变量。
是一个线程同步结构,用于在线程间传递信号,以避免出现信号丢失。
常用的信号量函数是下面5个:
#include
//初始化一个未命名的信号量
int sem_init(sem_t *sem,int shared,unsigned int value);
//销毁信号量,以释放其占用的内核资源
int sem_destroy(sem_t *sem);
//以原子操作的方式将信号量的值 -1。如果信号量的值为0,则该函数阻塞,直到该信号量具有非0值。
int sem_wait(sem_t *sem);
//与sem_wait函数类似,不过它始终立即返回,而不论被操作的信号量是否有非0值,相当于sem_wait的非阻塞版本
int sem_trywait(sem_t *sem);
//以原子操作的方式将信号量的值 +1。当信号量的值大于0时,其他正在调用sem_wait等待信号量的线程将被唤醒。
int sem_post(sem_t *sem);
这些函数的第一个参数 sem 指向被操作的信号量。
原子操作:是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何context switch(切换到另一个线程)
互斥锁(也称互斥量)可以用来保护关键代码段,以确保其独占式的访问。
互斥锁是只能在线程之间使用的一种控制临界资源访问的机制,如果一个线程要访问临界资源,则必须先加锁,用完之后解锁。这样,在一个线程访问临界资源的过程中,其他线程加锁就会阻塞,不能进入访问临界资源的临界区,直到访问临界资源的线程用完后并解锁。
互斥锁的相关函数主要有如下5个:
#include
//初始化互斥锁
int pthread_mutex_init(pthread_mutex_t* mutex,pthread_mutexattr_t *attr);
//销毁互斥锁,以释放其所占用的内核资源
int pthread_mutex_destroy(pthread_mutex_t* mutex);
//以原子操作给一个互斥锁加锁。如果目标互斥锁已经被锁上,则该函数调用将阻塞,直到该互斥锁的占有者将其解锁
int pthread_mutex_lock(pthread_mutex_t* mutex);
//与pthread_mutex_lock函数类似,不过它始终立即返回,而不论被操作的互斥锁是否已经被加锁
int pthread_mutex_trylock(pthread_mutex_t* mutex);
//以原子操作给一个互斥锁解锁。如果此时有其他线程正在等待这个互斥锁,则这些线程中的某一个将获得它
int pthread_mutex_unlock(pthread_mutex_t* mutex);
这些函数的第一个参数 mutex 指向要操作的目标互斥锁。
死锁:在一个线程中对一个已经加锁的普通锁再次加锁,将导致死锁。
死锁使得一个或多个线程被挂起而无法继续执行,而且这种情况还不容易被发现。
如果说互斥锁是用于同步线程对共享数据的访问的话,那么条件变量则是用于在线程之间同步共享数据的值。
条件变量提供了一种线程间的通知机制:当某个共享数组达到某个值的时候,唤醒等待这个共享数据的线程。
条件变量是线程的另外一种同步机制,这些同步对象为线程提供了会合的场所,理解起来就是两个(或者多个)线程需要碰头(或者说进行交互,一个线程给另外的一个或者多个线程发送消息),我们指定在条件变量这个地方发生,一个线程用于修改这个变量使其满足其它线程继续往下执行的条件,其它线程则接收条件已经发生改变的信号。
条件变量的相关函数主要有如下5个:
#include
//初始化条件变量
int pthread_cond_init(pthread_cond_t* cond,pthread_condattr_t *cond_attr);
//销毁条件变量
int pthread_cond_destroy(pthread_cond_t* cond);
//以广播的方式唤醒所有等待目标条件变量的线程
int pthread_cond_broadcast(pthread_cond_t* cond);
//唤醒一个等待目标变量的线程
int pthread_cond_signal(pthread_cond_t* cond);
//等待目标条件变量。mutex参数是用于保护条件变量的互斥锁,以确保该函数操作的原子性
int pthread_cond_wait(pthread_cond_t* cond,pthread_mutex_t* mutex);
这些函数的第一个参数 cond 指向要操作的目标条件变量。
条件变量需要和互斥锁一起使用。
为了充分复用代码,我们将前面讨论的3种同步机制分别封装成3个类,实现在 lock.h 文件中。
代码清单如下:
#ifndef LOCK_H
#define LOCK_H
#include
#include
#include
#include
#include
//信号量,是保护临界资源的一种方法,协调进程对共享资源的访问
//封装信号量的类
class Sem
{
private:
sem_t sem;
public:
Sem();
~Sem();
bool wait();
bool post();
};
Sem::Sem() //创建并初始化信号量
{
if (sem_init(&sem, 0, 0) != 0)//信号量的初始值和基于内存的信号量
std::cerr << "sem init error." << std::endl;
}
Sem::~Sem() //销毁信号量
{
sem_destroy(&sem);
}
bool Sem::wait() //等待信号量
{
return sem_wait(&sem) == 0 ? true : false;
}
bool Sem::post() //增加信号量
{
return sem_post(&sem) == 0 ? true : false;
}
//互斥锁:锁机制是同一时刻只允许一个线程执行一个关键部分的代码
//封装互斥锁的类
class Mutex
{
private:
pthread_mutex_t mutex;
public:
Mutex();
~Mutex();
bool mutex_lock(); //加锁
bool mutex_unlock(); //解锁
};
Mutex::Mutex() //创建并初始化互斥锁
{
//初始化锁,第二个参数指定锁的属性,为“NULL”则使用缺省属性(默认的互斥锁)
//可用PTHRAD_MUTEX_INITIALIZER宏初始化
//函数成功完成之后会返回零,其他任何返回值都表示出现了错误。
if (pthread_mutex_init(&mutex, NULL) != 0)
std::cerr << "mutex init error" << std::endl;
}
Mutex::~Mutex() //销毁锁
{
pthread_mutex_destroy(&mutex); //互斥锁销毁函数,在执行成功后返回 0,否则返回错误码
}
bool Mutex::mutex_lock() //加锁
{
return pthread_mutex_lock(&mutex) == 0 ? true : false;
//当pthread_mutex_lock()返回时,该互斥锁已被锁定。
//线程调用该函数让互斥锁上锁,如果该互斥锁已被另一个线程锁定和拥有,
//则调用该线程将阻塞,直到该互斥锁变为可用为止。
}
bool Mutex::mutex_unlock() //解锁
{
return pthread_mutex_unlock(&mutex) == 0 ? true : false;
//pthread_mutex_unlock()函数解除锁定 mutex 所指向的互斥锁
}
//条件变量的类
//封装条件变量的类
class Cond
{
private:
pthread_mutex_t mutex;
pthread_cond_t cond;
public:
Cond();
~Cond();
bool wait();
bool signal();
bool broadcast();
};
Cond::Cond() //创建并初始化条件变量
{
if (pthread_mutex_init(&mutex, NULL) != 0) //初始化互斥锁
{
std::cerr << "Cond mutex init error" << std::endl;
exit(0);
}
if (pthread_cond_init(&cond, NULL) != 0) //初始化条件变量
{
//构造函数中一旦出现问题,就应该立即释放已经成功分配了的资源
std::cerr << "Cond cond init error" << std::endl;
pthread_mutex_destroy(&mutex);
exit(0);
}
}
Cond::~Cond()
{
pthread_mutex_destroy(&mutex); //销毁锁
pthread_cond_destroy(&cond); //销毁条件变量
}
bool Cond::wait()
{
int rs = 0;
pthread_mutex_lock(&mutex); //调用互斥锁上锁
rs = pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
return rs == 0 ? true : false;
}
bool Cond::signal()
{
//pthread_cond_signal() 激活一个等待该条件的线程
return pthread_cond_signal(&cond) == 0 ? true : false;
}
bool Cond::broadcast()
{
//pthread_cond_broadcast() 激活所有等待线程
return pthread_cond_broadcast(&cond);
}