在学习Linux系统编程总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
09-linux-day09(线程同步)
一、内容回顾
1、守护进程:运行在后台,脱离终端,周期性执行某些任务
会话:多个进程组组成一个会话,组长进程不能创建会话。
进程组:多个进程在同一个组,第一个进程默认是组长
守护进程的步骤:
(1)创建子进程,父亲退出(孤儿进程)
(2)创建会话,当会长
(3)切换目录
(4)设置掩码
(5)关闭文件描述符
(6)执行核心逻辑
(7)退出
nohup & 把进程放入后台运行
2、多线程:
线程是轻量级的进程,最小的执行单位,进程是最小的资源申请单位。一个进程里可以有多个线程。
创建线程 pthread_create
回收线程 pthred_join
线程退出 pthread_exit void *retval
杀死线程 pthread_cancel 取消点
线程分离 pthread_detach,也可以通过属性设置
pthread_attr_setdetachstate 设置属性分离,之前需要pthread_attr_init初始化,之后需要pthread_attr_destroy销毁
查看线程ID:pthread_self 在进程内唯一
二、学习目标
1、熟练掌握互斥量的使用
2、说出什么叫死锁以及解决方案
3、熟练掌握读写死锁的使用
4、熟练掌握条件变量的使用
5、理解条件变量实现的生产者消费者模型
6、理解信号量实现的生产者消费者模型
三、线程同步
1、互斥量的使用
》互斥量
两个线程访问同一块共享资源,如果不协调顺序,容易造成数据混乱。
>加锁 mutex
pthread_mutex_init 初始化
pthread_mutex_destroy 摧毁
pthread_mutex_lock 加锁
pthread_mutex_unlock(pthread_mutex_t *mutex) 解锁
》互斥量的使用步骤:
1)初始化
2)加锁
3)执行逻辑——操作共享数据
4)解锁
注意事项:
加锁需要最小力度,不要一直占用临界区。
解决昨天的问题——模拟线程共同抢占(屏幕)资源
>vi pthread_print.c
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<pthread.h> 4 #include<stdlib.h> 5 6 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 7 8 9 10 void* thr1(void *arg){ 11 while(1){ 12 //先上锁 13 pthread_mutex_lock(&mutex);//加锁:当有线程已经加锁的时候会阻塞 14 //临界区 15 printf("hello");//不带换行,没有行缓冲,输出不出来 16 sleep(rand()%3); 17 printf("world\n"); 18 //释放锁 19 pthread_mutex_unlock(&mutex); 20 sleep(rand()%3); 21 } 22 } 23 24 void* thr2(void *arg){ 25 while(1){ 26 //先上锁 27 pthread_mutex_lock(&mutex); 28 //临界区 29 printf("HELLO"); 30 sleep(rand()%3); 31 printf("WORLD\n"); 32 //释放锁 33 pthread_mutex_unlock(&mutex); 34 sleep(rand()%3); 35 } 36 } 37 38 int main(int argc, char *argv[]) 39 { 40 //创建两个线程,回收两次 41 pthread_t tid[2]; 42 pthread_create(&tid[0],NULL,thr1,NULL); 43 pthread_create(&tid[1],NULL,thr2,NULL); 44 45 pthread_join(tid[0],NULL); 46 pthread_join(tid[1],NULL); 47 48 return 0; 49 }>make
>./pthread_print
》尝试加锁
man pthread_mutex_trylock
int pthread_mutex_trylock(pthread_mutex_t *mutex);
测试(已经加锁的再次尝试加锁结果会怎么样?)
>touch mutex_trylock.c
>vi mutex_trylock.c
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<pthread.h> 4 #include<string.h> 5 6 pthread_mutex_t mutex; 7 8 void *thr(void *arg) 9 { 10 while(1){ 11 pthread_mutex_lock(&mutex); 12 printf("hello world\n"); 13 sleep(30); 14 pthread_mutex_unlock(&mutex); 15 } 16 return NULL; 17 } 18 19 int main(int argc, char *argv[]) 20 { 21 pthread_mutex_init(&mutex,NULL); 22 pthread_t tid; 23 pthread_create(&tid,NULL,thr,NULL); 24 sleep(1); 25 while(1){ 26 int ret = pthread_mutex_trylock(&mutex); 27 if(ret > 0){ 28 printf("ret = %d,errmmsg:%s\n",ret,strerror(ret)); 29 } 30 sleep(1); 31 } 32 33 return 0; 34 }>make
>./mutex_trylock
(打印完hellow world后,一直打印:ret = 16,errmsg:Device or resource busy)
可以在以下文件查看errno的错误信息
2、死锁
》死锁
锁了又锁,自己加了一次锁成功,又加了一次锁。
交叉锁(解决办法:每个线程申请锁的顺序要一致。如果申请到一把锁,申请另外一把的时候申请失败,应该释放已经掌握的。)
注意:互斥量只是建议锁。
3、读写锁
》读写锁
与互斥量类似,但读写锁允许更高的并行性。其特点为:读共享,写独占,写优先级高。
》读写锁状态:
三种状态:
1)读模式下加锁状态(读锁)
2)写模式下加锁状态(写锁)
3)不加锁状态
》读写锁特性:
1)读写锁是“写模式加锁”时,解锁前,所有对该锁加锁的线程都会被阻塞。
2)读写锁是“读模式加锁”时,如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。
3)读写锁是“读模式加锁”时,既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高。
读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。读共享,写独占。
》读写锁的使用场景:非常适合于对数据结构读的次数远大于写的情况。
》读写锁场景练习:
1)线程A加写锁成功,线程B请求读锁
B阻塞
2)线程A持有读锁,线程B请求写锁
B阻塞
3)线程A拥有读锁,线程B请求读锁
B加锁成功
4)线程A持有读锁,然后线程B请求写锁,然后线程C请求写锁
BC阻塞;A释放后,B加锁;B释放后,C加锁
5)线程A持有写锁,然后线程B请求读锁,然后线程C请求写锁
BC阻塞;A释放后,C加锁;C释放后,B加锁
初始化:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
或
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
4、条件变量介绍和生产者和消费者模型
5、条件变量生产者消费者模型实现
6、条件变量生产者和消费者模型演示
7、信号量的概念和函数
8、信号量实现生产者和消费者分析
9、信号量实现生产者和消费者
10、文件锁单开进程
11、哲学家就餐模型分析
在学习Linux系统编程总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
