1:概念
1.1来源:资源竞争
#define MAX 100
int number;
void *funcA(void* arg){
int i;
for(i=0;i<MAX ; ++i){
int cur=number;
cur++;
number=cur;
printf("pthread A,ID=%lu number=%d \n",pthread_self(),number);
usleep(10);
}
return NULL;
}
void *funcB(void* arg){
int i;
for(i=0;i<MAX ; ++i){
int cur=number;
cur++;
number=cur;
printf("pthread B,ID=%lu number=%d \n",pthread_self(),number);
usleep(10);
}
return NULL;
}
int main(){
pthread_t p1,p2;
//创建两个子进程
pthread_create(&p1,NULL,funcA,NULL);
pthread_create(&p2,NULL,funcB,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
return 0;
}
1.2思想
2互斥量
- 互斥锁类型:pthread_mutex
- 特点:保证多个线程串行的访问共享数据
- 缺点:效率低
2.0互斥锁使用步骤
- 创建锁:pthread_mutex_t mutex
- 初始化:pthread_mutex_init(&mutex, NULL)==mutex=1(相当于有一把锁可以用)
- 寻找共享资源:
- 在操作共享资源代码之前加锁int pthread_mutex_lock(pthread_t *mute)–mutex=0
- 在操作共享资源代码之后解锁int pthread_mutex_unlock(pthread_t *mute)–mutex=1
2.1分类:
分类 | 实现 | 特点 |
---|---|---|
静态分配互斥量 | pthread_mutex_t mutex= PTHREAD_MUTEX_INITIALIZER; | 简单 |
动态分配互斥量 | pthread_mutex_init(&mutex, NULL);pthread_mutex_destroy(&mutex); | 可以设置更多的选项 |
2.1加锁 int pthread_mutex_lock(pthread_t *mutex)
- mutex没有被上锁,当前线程会将这把锁给锁上
- 如果加锁的时候发现mutex锁已经被锁上了,线程就一直会阻塞在这个位置
- 锁被打开的时候才会取消阻塞,而阻塞的时调用这个函数的线程
2.2尝试加锁 int pthread_mutex_trylock(pthread_t *mutex)
- 判断这把锁是不是锁上了
- 如果没锁上,当前线程会给这把锁锁上
- 如果锁上了,不会阻塞,返回值
- 返回值:成功:0 失败:错误号
2.4解锁 int pthread_mutex_unlock(pthread_t *mutex)
int number;
//创建一把互斥锁
pthread_mutex_t mutex;
void *funcA(void* arg){
//加锁
pthread_mutex_lock(&mutex);
int i;
for(i=0;i<MAX ; ++i){
int cur=number;
cur++;
number=cur;
printf("pthread A,ID=%lu number=%d \n",pthread_self(),number);
usleep(10);
}
//解锁
pthread_mutex_unlock(&mutex);
return NULL;
}
void *funcB(void* arg){
//加锁
pthread_mutex_lock(&mutex);
int i;
for(i=0;i<MAX ; ++i){
int cur=number;
cur++;
number=cur;
printf("pthread B,ID=%lu number=%d \n",pthread_self(),number);
usleep(10);
}
//解锁
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(){
pthread_t p1,p2;
//初始化
pthread_mutex_init(&mutex,NULL);
//创建两个子进程
pthread_create(&p1,NULL,funcA,NULL);
pthread_create(&p2,NULL,funcB,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
//销毁互斥锁
pthread_mutex_destroy(&mutex);
return 0;
}
3死锁
3.1造成死锁的原因
- 自己锁自己
pthread_mutex_lock(&mutex);
pthread_mutex_lock(&mutex);
int i;
for(i=0;i<MAX ; ++i){
int cur=number;
cur++;
number=cur;
printf("pthread A,ID=%lu number=%d \n",pthread_self(),number);
usleep(10);
}
//解锁
pthread_mutex_unlock(&mutex);
- 多个线程
- 互斥条件
- 请求和保持条件
- 不可剥夺条件
- 环路等待条件
3.2结果方案
- 让现场按照一定的顺序去访问共享资源
- 访问其他锁的时候,先将自己的锁解开
- trylock()
4读写锁
4.1概念
- 读写锁是一把锁: pthread_rwlock_t lock;
4.2读写锁的类型
- 读锁->对内存做读操作
- 写锁->对内存做写操作
4.3特点:共享独占
- 读取锁(共享)-并行处理
- 写入锁(独占)
- 读写不能同时
- 写的优先级比较高
4.4读写锁场景练习
- 线程A加写锁成功,线程B请求读锁
- 线程B阻塞
- 线程A持有读锁,线程B请求写锁
- 线程B阻塞
- 线程A持有读锁,线程B请求读锁
- 线程B加锁成功
- 线程A持有读锁,然后线程B请求写锁,然后线程C请求读锁
- B阻塞 C阻塞(因为写操作优先级高)
- A解索,B线程加锁成功 C继续阻塞
- 线程A持有写锁,然后线程B请读锁,然后线程C请求写锁
- B阻塞 C阻塞
- A解锁 C加锁成功 B继续阻塞
- C解锁 B加锁成功
4.5 使用场景
- 互斥锁:读写串行
- 程序中的读操作大于写操作的时候
4.6主要操作函数
- 初始化
- pthread_rwlock_init(&rwlock, NULL);
- 销毁
- pthread_rwlock_destroy(&rwlock);
- 读取锁
- int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
- 阻塞:之前对这把锁加的写锁的操作
- int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
- 加锁成功:0
- 失败:错误号
- int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
- 写入锁
- int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
- int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
- 解锁
- int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)。
4.7练习
int number=0;
pthread_rwlock_t lock;
void* write_func(void *arg){
//循环写
while(1){
//加写锁
pthread_rwlock_wrlock(&lock);
number++;
printf("==write: %lu, %d\n",pthread_self(),number);
pthread_rwlock_unlock(&lock);
usleep(500);
}
return NULL;
}
void* read_func(void *arg){
while(1){
pthread_rwlock_rdlock(&lock);
printf("==read: %lu, %d\n",pthread_self(),number);
pthread_rwlock_unlock(&lock);
usleep(500);
}
return NULL;
}
int main(){
int i;
//初始化读写锁
pthread_rwlock_init(&lock,NULL);
pthread_t p[8];
//创建3个写线程
for(i=0;i<3;i++){
pthread_create(&p[i],NULL,write_func,NULL);
}
//创建5个读线程
for(i=3;i<8;i++){
pthread_create(&p[i],NULL,read_func,NULL);
}
//阻塞回收子线程的pcb
for(i=0;i<8;i++){
pthread_join(p[i],NULL);
}
pthread_rwlock_destroy(&lock);
return 0;
}
5条件变量
5.1概念
- 本质:不是锁,只是一组函数 ,但是这个函数(条件变量)能够阻塞线程
- 如果要达到线程同步:使用条件变量+互斥锁
- 互斥量:保护一块共享资源
- 条件变量:引起阻塞
5.2条件变量的两个动作
- 条件不满足时:阻塞线程
- 条件满足时:通知阻塞阻塞线程开始工作
5.3条件变量的类型
pthread_cond_t cond;
5.4 操作函数
-
初始化:pthread_cond_init(pthread_cond_t *cond, NULL);
-
销毁pthread_cond_destroy(pthread_cond_t * cond);
-
条件等待 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
- 阻塞线程
- 将线程开始阻塞,将已经上锁的互斥锁解锁
- 该线程解除阻塞,会对解锁的互斥锁加锁
-
计时等待 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
- 绝对时间:在到达绝对时间后,阻塞函数将自动解除阻塞,不再阻塞线程
- 返回值 : ETIMEDOUT 超时结束等待
正数| 错误码
- 单个激活 int pthread_cond_signal(pthread_cond_t *cond)
- 返回值
返回值 | 含义 |
---|---|
0 | 成功 |
正数 | 错误码 |
- 全部激活 int pthread_cond_broadcast(pthread_cond_t *cond)
- 返回值
返回值 | 含义 |
---|---|
0 | 成功 |
5.5流程
void* produce(void* arg){
typedef struct{
pthread_mutex_t mutex;
pthread_cond_t cond;
int count;
bool condition;
}Data;
void * child(void * arg){
Data pdata=(Data *)arg;
for(;;){
pthread_mutex_lock(&mutex);
printf("child enter \n");
++pdata->count;
printf("count =%d\n",pdata->count);
sleep(1);
if(count%3==0){
condition=true;
pthread_cond_signal(&pdata->cond);
}
pthread_mutex_unlock(&pdata->mutex);
printf("child leave\n");
usleep(10);
}
}
void parent(void *arg){
Data * pdata=(Data *)arg;
for(;;){
pthread_mutex_lock(&pdata->mutex);\
uh
printf("parent enter\n");
while(!pdata->condition){
printf("wait single\n");
pthread_cond_wait(&pdata->cond,&pdata->mutex);
}
pdata->condition=false;
printf("recieve single\n");
pthread_mutex_unlock(&pdata->mutex);
printf("parent leave\n");
}
}
int main(){
Data data
pthread_mutex_init(&data,mutex,NULL);
pthread_cond_init(&data,cond,NULL);
data.cond=0;
data.condition=false;
ty
}
}