1.互斥锁
含义:用于保护临界区,确保同一时间只有一个线程访问数据。对共享资源的访问,先对互斥量进行加锁,如果互斥量已经上锁,调用线程会阻塞,直到互斥量被解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
互斥锁特点:
初始化锁:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
//参数attr为指定锁的属性
实例:
pthread_mutex_init(&mutex,NULL);
如果属性为NULL,则使用缺省属,将会有以下4种值可选:
(1)PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
(2)PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
(3)PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
(4)PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。
阻塞加锁
int pthread_mutex_lock(pthread_mutex *mutex);
非阻塞加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);`
与阻塞加锁不同的是在锁已经被占据时返回 EBUSY 而不是挂起等待。
解锁
int pthread_mutex_unlock(pthread_mutex *mutex);
销毁锁
int pthread_mutex_destroy(pthread_mutex *mutex);
互斥锁的优缺点:
优点:使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。
缺点:①互斥量是可以命名的,也就是说它可以跨越进程使用,所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它
2.条件变量
示意图:
条件变量用来自动阻塞一个线程,直 到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:
1、一个线程等待"条件变量的条件成立"而挂起;
2、另一个线程使 “条件成立”(给出条件成立信号)唤醒等待线程。
原理:
条件的检测是在互斥锁的保护下进行的。线程在改变条件状态之前必须首先锁住互斥量。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量 可以被用来实现这两进程间的线程同步。
操作流程如下:
1、初始化:init()或者pthread_cond_tcond=PTHREAD_COND_INITIALIER;属性置为NULL;
2、等待条件成立:pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真 timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait);
3、激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
4、清除条件变量:destroy;无线程等待,否则返回EBUSY。
初始化:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//因此cond_attr值通常为NULL,且被忽略。
1、无条件等待函数
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
2、计时等待函数
int pthread_cond_timewait(pthread_cond_t *cond,
pthread_mutex *mutex,const timespec *abstime);
如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求竞争条件(Race Condition)。mutex互斥锁必须是普通锁或者适应锁,且在调用pthread_cond_wait()前必须由本线程加锁,而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。
激发条件
1、激活一个等待该条件的线程(存在多个等待线程时按入队顺序激活其中一个)
int pthread_cond_signal(pthread_cond_t *cond);//通过信号量
2、激活所有等待线程
int pthread_cond_broadcast(pthread_cond_t *cond);
销毁条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
注意:只有在没有线程在该条件变量上等待的时候才能销毁这个条件变量,否则返回EBUSY。
说明
1、pthread_cond_wait 自动解锁互斥量(如同执行了pthread_unlock_mutex),并等待条件变量触发。这时线程挂起,不占用CPU时间,直到条件变量被触发(变量为ture)。在调用 pthread_cond_wait之前,应用程序必须加锁互斥量。pthread_cond_wait函数返回前,自动重新对互斥量加锁(如同执行了pthread_lock_mutex)。
2、互斥量的解锁和在条件变量上挂起都是自动进行的。因此,在条件变量被触发前,如果所有的线程都要对互斥量加锁,这种机制可保证在线程加锁互斥量和进入等待条件变量期间,条件变量不被触发。条件变量要和互斥量相联结,以避免出现条件竞争,即一个线程预备等待一个条件变量,当它在真正进入等待之前,另一个线程恰好触发了该条件(条件满足信号有可能在测试条件和调用pthread_cond_wait函数(block)之间被发出,从而造成无限制的等待)。
3、条件变量函数不是异步信号安全的,不应当在信号处理程序中进行调用。特别要注意,如果在信号处理程序中调用pthread_cond_signal 或 pthread_cond_boardcast 函数,可能导致调用线程死锁。
代码实例:
pthread_mutex_t mutex;//互斥锁
pthread_cond_t cond;//条件变量
void * fun1(void* arg)
{
char * s = (char*)arg;
while(1)
{
pthread_mutex_lock(&mutex);//加锁
pthread_cond_wait(&cond,&mutex);//将自己添加到条件变量的等待队列上,执行完程序阻塞住,在main函数中唤醒
pthread_mutex_unlock(&mutex);//解锁
printf("fun1:buff=%s\n",s);
}
}
void * fun2(void* arg)
{
char * s = (char*)arg;
while(1)
{
pthread_mutex_lock(&mutex);//加锁
pthread_cond_wait(&cond,&mutex);//将自己添加到条件变量的等待队列上,执行完程序阻塞住,在main函数中唤醒
pthread_mutex_unlock(&mutex);//解锁
printf("fun2:buff=%s\n",s);
}
}
int main()
{
pthread_t id[2];//启动两个线程
char buff[128] = {0};
//锁的初始化
pthread_mutex_init(&mutex,NULL);
//条件变量初始化
pthread_cond_init(&cond,NULL);
//fun1线程函数
pthread_create(&id[0],NULL,fun1,NULL);
pthread_create(&id[1],NULL,fun2,NULL);
while(1)
{
fgets(buff,128,stdin);
//唤醒线程
pthread_cond_signal(&cond);
}
}
3.读写锁
初始化
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
https://blog.csdn.net/ZYZMZM_/article/details/88771539
4.信号量
初始化信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
sem:指定要初始化的信号量;
pshared:信号量 sem 的共享选项,linux只支持0,表示它是当前进程的局部信号量;
value:信号量 sem 的初始值。
p操作(给参数sem指定的信号量值加1)
#include <semaphore.h>
int sem_post(sem_t *sem);
v操作(给参数sem指定的信号量值减1)
#include <semaphore.h>
int sem_wait(sem_t *sem);
销毁信号量
#include <semaphore.h>
int sem_destroy(sem_t *sem);