长春市做网站的公司,成都建站网址,做挂件像网站,公司名称大全简单大气易经起名目录
复习目标#xff1a;
1 互斥锁
1.1互斥锁的使用步骤
1.2 练习
1.3 死锁
2 读写锁
3 条件变量
4 信号量 复习目标#xff1a;
熟练掌握互斥量的使用说出什么叫死锁以及解决方案熟练掌握读写锁的使用熟练掌握条件变量的使用理解条件变量实现的生产消费者模型理解…目录
复习目标
1 互斥锁
1.1互斥锁的使用步骤
1.2 练习
1.3 死锁
2 读写锁
3 条件变量
4 信号量 复习目标
熟练掌握互斥量的使用说出什么叫死锁以及解决方案熟练掌握读写锁的使用熟练掌握条件变量的使用理解条件变量实现的生产消费者模型理解信号量实现的生产消费者模型
1 互斥锁
1.1互斥锁的使用步骤
第1步创建一把互斥锁
pthread_mutex_t mutex;
初始化互斥锁
pthread_mutex_init(mutex);---相当于mutex1
在代码中寻找共享资源也称为临界区
pthread_mutex_lock(mutex); -- mutex 0
[临界区代码]
pthread_mutex_unlock(mutex); -- mutex 1
释放互斥锁资源
pthread_mutex_destroy(mutex);
注意必须在所有操作共享资源的线程上都加上锁否则不能起到同步的效果 #include stdlib.h
#include string.h
#include stdio.h
#include sys/types.h
#include unistd.h
#include pthread.h
#include time.h
#include sys/types.h//定义一把锁
pthread_mutex_t mutex;void *mythread1(void *args)
{while (1){//加锁pthread_mutex_lock(mutex);printf(hello );sleep(rand() % 3);printf(world\n);//解锁pthread_mutex_unlock(mutex);sleep(rand() % 3);}pthread_exit(NULL);
}void *mythread2(void *args)
{while (1){//解锁pthread_mutex_lock(mutex);printf(HELLO);sleep(rand() % 3);printf(WORLD\n);//解锁pthread_mutex_unlock(mutex);sleep(rand() % 3);}pthread_exit(NULL);
}int main()
{int ret;pthread_t thread1;pthread_t thread2;//随机数种子srand(time(NULL));//互斥锁初始化pthread_mutex_init(mutex, NULL);ret pthread_create(thread1, NULL, mythread1, NULL);if (ret ! 0){printf(pthread_create error ,[%s]\n, strerror(ret));return -1;}ret pthread_create(thread2, NULL, mythread2, NULL);if (ret ! 0){printf(pthread_create error ,[%s]\n, strerror(ret));return -1;}//等待线程结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);//释放互斥锁pthread_mutex_destroy(mutex);system(pause);return 0;
} 1.2 练习
编写思路
1 定义一把互斥锁应该为一全局变量
pthread_mutex_t mutex;
2 在main函数中对mutex进行初始化
pthread_mutex_init(mutex, NULL);
3 创建两个线程在两个线程中加锁和解锁
4 主线程释放互斥锁资源
pthread_mutex_destroy(mutex); 1.3 死锁
死锁并不是linux提供给用户的一种使用方法而是由于用户使用互斥锁不当引起的一种现象。
常见的死锁有两种
第一种自己锁自己如下图代码片段 第二种 线程A拥有A锁请求获得B锁线程B拥有B锁请求获得A锁这样造成线程A和线程B都不释放自己的锁而且还想得到对方的锁从而产生死锁如下图所示 如何解决死锁
让线程按照一定的顺序去访问共享资源在访问其他锁的时候需要先将自己的锁解开调用pthread_mutex_trylock如果加锁不成功会立刻返回
2 读写锁
什么是读写锁
读写锁也叫共享-独占锁。当读写锁以读模式锁住时它是以共享模式锁住的当它以写模式锁住时它是以独占模式锁住的。写独占、读共享。
读写锁使用场合
读写锁非常适合于对数据结构读的次数远大于写的情况。
读写锁特性
读写锁是“写模式加锁”时解锁前所有对该锁加锁的线程都会被阻塞。读写锁是“读模式加锁”时如果线程以读模式对其加锁会成功如果线程以写模式加锁会阻塞。读写锁是“读模式加锁”时 既有试图以写模式加锁的线程也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞写锁优先级高
读写锁场景练习:
线程A加写锁成功, 线程B请求读锁
线程B阻塞
线程A持有读锁, 线程B请求写锁
线程B阻塞
线程A拥有读锁, 线程B请求读锁
线程B加锁成功
线程A持有读锁, 然后线程B请求写锁, 然后线程C请求读锁
B阻塞c阻塞 - 写的优先级高
A解锁B线程加写锁成功C继续阻塞B解锁C加读锁成功
线程A持有写锁, 然后线程B请求读锁, 然后线程C请求写锁
BC阻塞A解锁C加写锁成功B继续阻塞C解锁B加读锁成功
读写锁总结
读并行写独占当读写同时等待锁的时候写的优先级高
读写锁主要操作函数
定义一把读写锁
pthread_rwlock_t rwlock;
初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);函数参数
rwlock-读写锁attr-读写锁属性传NULL为默认属性
销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
加读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
尝试加读锁
int pthread_rwlock_tryrdlock(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);
练习3个线程不定时写同一全局资源5个线程不定时读同一全局资源。
#include stdlib.h
#include string.h
#include stdio.h
#include sys/types.h
#include unistd.h
#include pthread.h
#include sys/types.h//读写锁测试程序
int number 0;//定义一把读写锁
pthread_rwlock_t rwlock;//读写锁的回调函数
void *thread_write(void *arg)
{int i *(int *)arg;int cur;while (1){//加读写锁pthread_rwlock_wrlock(rwlock);cur number;cur;number cur;printf([%d]-W:[%d]\n, i, cur);//解锁pthread_rwlock_unlock(rwlock);sleep(rand() % 3);}
}//读回调函数
void *thread_read(void *arg)
{int i *(int *)arg;int cur;while (1){//加读锁pthread_rwlock_rdlock(rwlock);cur number;printf([%d]-R:[%d]\n, i, cur);//解锁// pthread_rwlock_unlock(rwlock);pthread_rwlock_unlock(rwlock);sleep(rand() % 3);}
}int main()
{int n 8;int i 0;int arr[8];pthread_t thread[8];//读写锁初始化pthread_rwlock_init(rwlock, NULL);//创建3个写子线程for (i 0; i 3; i){arr[i] i;pthread_create(thread[i], NULL, thread_write, arr[i]);}//创建5个读子线程for (i 3; i n; i){arr[i] i;pthread_create(thread[i], NULL, thread_read, arr[i]);}//回收子线程int j 0;for (j 0; j n; j){pthread_join(thread[j], NULL);}//释放锁pthread_rwlock_destroy(rwlock);system(pause);return 0;
} 3 条件变量 条件本身不是锁但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。使用互斥量保护共享数据;使用条件变量可以使线程阻塞, 等待某个条件的发生, 当条件满足的时候解除阻塞.条件变量的两个动作:条件不满足, 阻塞线程条件满足, 通知阻塞的线程解除阻塞, 开始工作.条件变量相关函数pthread_cond_t cond;定义一个条件变量int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);函数描述:初始化条件变量函数参数: 函数返回值:成功返回0, 失败返回错误号int pthread_cond_destroy(pthread_cond_t *cond);函数描述: 销毁条件变量函数参数: 条件变量返回值: 成功返回0, 失败返回错误号int pthread_cond_wait(pthread_cond_t *restrict cond,函数描述: 条件不满足, 引起线程阻塞并解锁;函数参数:函数返回值: 成功返回0, 失败返回错误号 int pthread_cond_signal(pthread_cond_t *cond);函数描述: 唤醒至少一个阻塞在该条件变量上的线程函数参数: 条件变量函数返回值: 成功返回0, 失败返回错误号
cond: 条件变量
attr: 条件变量属性, 通常传NULL pthread_mutex_t *restrict mutex); 条件满足, 解除线程阻塞, 并加锁
cond: 条件变量
mutex: 互斥锁变量 使用条件变量的代码片段 上述代码中生产者线程调用pthread_cond_signal函数会使消费者线程在pthread_cond_wait处解除阻塞。
//使用条件变量实现生产者和消费者模型
#include stdlib.h
#include string.h
#include stdio.h
#include sys/types.h
#include unistd.h
#include pthread.h
#include sys/types.htypedef struct node
{int data;struct node *next;
} NODE;NODE *head NULL;//定义一把锁
pthread_mutex_t mutex;//定义条件变量
pthread_cond_t cond;//生产者线程
void *producer(void *arg)
{NODE *pNode NULL;while (1){pNode (NODE *)malloc(sizeof(NODE));if (pNode NULL){perror(malloc error);exit(-1);}pNode-data rand() % 1000;printf(P:[%d]\n, pNode-data);//加锁pthread_mutex_lock(mutex);pNode-next head;head pNode;//解锁pthread_mutex_unlock(mutex);//通知消费者线程解除阻塞pthread_cond_signal(cond);sleep(rand() % 3);}
}//消费者线程
void *consumer(void *arg)
{NODE *pNode NULL;while (1){//加锁pthread_mutex_lock(mutex);if (head NULL){//若条件不满足,需要阻塞等待//若条件不满足,则阻塞等待并解锁;//若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁pthread_cond_wait(cond, mutex);}printf(C:[%d]\n, head-data);pNode head;head head-next;//解锁pthread_mutex_unlock(mutex);free(pNode);pNode NULL;sleep(rand() % 3);}
}int main()
{int ret;pthread_t thread1;pthread_t thread2;//初始化互斥锁pthread_mutex_init(mutex, NULL);//条件变量初始化pthread_cond_init(cond, NULL);//创建生产者线程ret pthread_create(thread1, NULL, producer, NULL);if (ret ! 0){printf(pthread_create error,[%s]\n, strerror(ret));return -1;}//创建消费者线程ret pthread_create(thread2, NULL, consumer, NULL);if (ret ! 0){printf(pthread_create error,[%s]\n, strerror(ret));return -1;}//等待线程结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);//释放互斥锁pthread_mutex_destroy(mutex);//释放条件变量pthread_cond_destroy(cond);system(pause);return 0;
}
多个生产者和消费者
//使用条件变量实现多个生产者和消费者模型
#include stdio.h
#include stdlib.h
#include string.h
#include sys/types.h
#include unistd.h
#include pthread.h
typedef struct node
{int data;struct node *next;
}NODE;NODE *head NULL;//定义一把锁
pthread_mutex_t mutex;//定义条件变量
pthread_cond_t cond;//生产者线程
void *producer(void *arg)
{NODE *pNode NULL;int n *(int *)arg;while(1){//生产一个节点pNode (NODE *)malloc(sizeof(NODE));if(pNodeNULL){perror(malloc error);exit(-1);}pNode-data rand()%1000;printf(P[%d]:[%d]\n, n, pNode-data);//加锁pthread_mutex_lock(mutex);pNode-next head;head pNode;//解锁pthread_mutex_unlock(mutex);//通知消费者线程解除阻塞pthread_cond_signal(cond);sleep(rand()%3);}
}//消费者线程
void *consumer(void *arg)
{NODE *pNode NULL;int n *(int *)arg;while(1){//加锁pthread_mutex_lock(mutex);if(headNULL){//若条件不满足,需要阻塞等待//若条件不满足,则阻塞等待并解锁;//若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁 pthread_cond_wait(cond, mutex);}if(headNULL){//解锁pthread_mutex_unlock(mutex); continue;}printf(C[%d]:[%d]\n, n, head-data); pNode head;head head-next;//解锁pthread_mutex_unlock(mutex);free(pNode);pNode NULL;sleep(rand()%3);}
}int main()
{int ret;int i 0;pthread_t thread1[5];pthread_t thread2[5];//初始化互斥锁pthread_mutex_init(mutex, NULL);//条件变量初始化pthread_cond_init(cond, NULL);int arr[5];for(i0; i5; i){arr[i] i;//创建生产者线程ret pthread_create(thread1[i], NULL, producer, arr[i]);if(ret!0){printf(pthread_create error, [%s]\n, strerror(ret));return -1;}//创建消费者线程ret pthread_create(thread2[i], NULL, consumer, arr[i]);if(ret!0){printf(pthread_create error, [%s]\n, strerror(ret));return -1;}}//等待线程结束for(i0; i5; i){pthread_join(thread1[i], NULL);pthread_join(thread2[i], NULL);}//释放互斥锁pthread_mutex_destroy(mutex);//释放条件变量pthread_cond_destroy(cond);return 0;
}4 信号量
1 信号量介绍
信号量相当于多把锁, 可以理解为是加强版的互斥锁
2 相关函数
定义信号量 sem_t sem; int sem_init(sem_t *sem, int pshared, unsigned int value);
pshared: 0表示线程同步, 1表示进程同步
value: 最多有几个线程操作共享数据
sem: 信号量变量 3 信号量代码片段: 函数描述: 初始化信号量函数参数:函数返回值:成功返回0, 失败返回-1, 并设置errno值int sem_wait(sem_t *sem);函数描述: 调用该函数一次, 相当于sem--, 当sem为0的时候, 引起阻塞函数参数: 信号量变量函数返回值: 成功返回0, 失败返回-1, 并设置errno值int sem_post(sem_t *sem);函数描述: 调用一次, 相当于sem函数参数: 信号量变量函数返回值: 成功返回0, 失败返回-1, 并设置errno值int sem_trywait(sem_t *sem);函数描述: 尝试加锁, 若失败直接返回, 不阻塞函数参数: 信号量变量函数返回值: 成功返回0, 失败返回-1, 并设置errno值int sem_destroy(sem_t *sem);函数描述: 销毁信号量函数参数: 信号量变量函数返回值: 成功返回0, 失败返回-1, 并设置errno值
//使用信号量实现生产者和消费者模型
#include stdlib.h
#include string.h
#include stdio.h
#include sys/types.h
#include unistd.h
#include pthread.h
#include sys/types.h
//信号量的头文件
#include semaphore.htypedef struct node
{int data;struct node *next;
} NODE;NODE *head NULL;//定义信号量
sem_t sem_producer;
sem_t sem_consumer;//生产者线程void *producer(void *arg)
{NODE *pNode NULL;while (1){pNode (NODE *)malloc(sizeof(NODE));if (pNode NULL){perror(malloc error);exit(-1);}pNode-data rand() % 100;printf(P;[%d]\n, pNode-data);//加锁sem_wait(sem_producer);pNode-next head;head pNode;//解锁sem_post(sem_consumer);sleep(rand() % 3);}
}void *consumer(void *arg)
{NODE *pNode NULL;while (1){//加锁sem_wait(sem_consumer); //相当于--printf(C:[%d]\n, head-data);pNode head;head head-next;//解锁sem_post(sem_producer); //相当于free(pNode);pNode NULL;sleep(rand() % 3);}
}int main()
{int ret;pthread_t thread1;pthread_t thread2;//初始化信号量sem_init(sem_producer, 0, 5);sem_init(sem_consumer, 0, 0);//创建生产者线程ret pthread_create(thread1, NULL, producer, NULL);if (ret ! 0){printf(pthread create error,[%s]\n, strerror(ret));return -1;}ret pthread_create(thread2, NULL, consumer, NULL);if (ret ! 0){printf(pthread create error,[%s]\n, strerror(ret));return -1;}//等待线程结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);//释放信号量资源sem_destroy(sem_producer);sem_destroy(sem_consumer);system(pause);return 0;
}