天天看点

Linux系统编程——09-linux-day09(线程同步)

在学习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的错误信息

Linux系统编程——09-linux-day09(线程同步)
Linux系统编程——09-linux-day09(线程同步)

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、哲学家就餐模型分析