本章学习内容
1.线程概念
2.线程同步(信号、互斥锁、读写锁、条件变量)
3.线程安全
4.线程与进程(fork)结合
一、线程概念
1.线程概念:进程内部的一条执行路径 头文件:pthread.h
e.g:main()函数中按流程执行下来的称作:主线程 创建新的线程执行称作:线程函数(子线程)
2.线程实现方式
在操作系统中,线程的实现方式分三种方式
(1)内核级线程 优点:可以使用多个处理器(可以实现并行) 缺点:创建开销大
(2)用户级线程 优点:创建开销小 缺点:无法使用多个处理器
(3)组合级线程
Linux操作系统线程属于内核级线程
3.线程相关接口
pthread_create() 创建线程
pthread_join() 等待线程
pthread_exit() 退出线程
二、线程并发运行
线程同步(通讯)
概念:线程同步指的是当一个线程在对某个临界资源进行操作时,其他线程都不可以对这个资源进行操作, 直到该线程完成操作, 其他线程才能操作, 也就是协同步调, 让线程按预定的先后次序进行运行。
线程同步方式有:信号、互斥锁、读写锁、条件变量
1.信号量(通过信号量控制线程同步)
(1)线程同步的信号量不需要自己封装(多进程通信的信号量需要自己封装)
(2)线程同步信号量相关接口
sem_init() 信号量初始化
sem_wait() p操作
sem_post() v操作
sem_destroy() 销毁信号量
信号线程同步的代码实现
结果如下
代码运行n次结果都相同,所以线程同步成功
2.互斥锁
(1)与信号量相似,互斥锁通过上锁与解锁控制线程同步运
(2)互斥锁相关接口
pthread_mutex_init() 互斥锁初始化
pthread_mutex_lock() 上锁
pthread_mutex_unlock() 解锁
pthread_mutex_destroy() 销毁锁
互斥锁实现线程同步代码实现
结果如下
与信号控制线程同步结果相同
3.读写锁(读操作多写操作少好用)
(1)读写锁的写操作一次只能一个线程去写,读可以多个线程同时读
(2)读写锁相关接口
pthread_rwlock_init() 读写锁初始化
pthread_rwlock_rdlock() 加读锁
pthread_rwlock_wrlock() 加写锁
pthread_rwlock_unlock() 解锁
pthread_rwlock_destroy() 销毁锁
读写锁同步进程代码实现
结果如下
总结:如上结果所示,在执行写操作时开始与结束中间都没有读操作,证明在线程执行写操作时,不能执行读操作,而读操作可以多线程同时去读。
4.条件变量
(1)条件变量概念:条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值时,唤醒等待这个共享数据的线程
(2)条件变量的使用会伴随着互斥锁的使用
(3)条件变量相关接口
pthread_cond_init() 初始化条件变量
pthread_cond_wait() 阻塞时条件变量内部先解锁再加锁
pthread_cond_signal() 唤醒某单个线程
pthread_cond_broadcast() 唤醒所有等待的线程
pthread_cond_destroy() 销毁条件变量
代码实现条件变量
结果如下
如上,当代码运行后,输入hello,触发任意一个线程的唤醒条件,输入end(代码中设置的)同时触发两个线程条件 的唤醒条件,结束程序
三、线程安全
1.概念:线程安全即就是在多线程运行的时候, 不论线程的调度顺序怎样, 最终的结果都是一样的、正确的。那么就说这些线程是安全的。
2.如何保证线程安全:
(1)对线程同步, 保证同一时刻只有一个线程访问临界资源。
(2)在多线程中使用线程安全的函数(可重入函数),所谓线程安全的函数指的是: 如果一个函数能被多个线程同时调用且不发生竟态条件, 则我们程它是线程安全的。(e.g:strtok()函数不安全,多线程时用strtok_r()替代)
代码实现保证线程安全
不安全线程代码如下
不安全线程代码结果如下
安全线程代码如下
安全线程代码结果如下
结论:由于strtok在多线程使用时,字符串分割后,系统在线程中自动匹配上次分割位置,但由于该程序为多线程,系统匹配再分割位置可能匹配到别的线程,所以会出现不安全线程
四、线程与fork
(1)多线程+fork,只启用fork所在的线程进行子进程的复制
(2)父进程加锁fork后,子进程也会将锁复制过来