Linux环境编程--多线程的工作原理以及多线程锁

多线程编程作用(使用目的)

 线程是进程的一条执行路径,在UNIX中,线程是轻量级的进程。这点与windows不同。典型的UNIX进程可以看成只有一个主线程: 一个进程在某一时刻只能做一件事。

 有了多线程之后,我们可以让一个进程同一时刻做不止一件事,每个线程处理各自独立的任务。

 

多线程与多进程之间的区别:

 1.创建的方式不相同,多进程调用vfork()/fork()进行创建而多线程是调用pthread_create()创建。

 2.进程是创建多个子进程空间和内存都独立。多个线程是在一个进程的情况下完成多个任务,并且进程定义的全局变量,所属的所有线程都可以对它进行修改。

 3.由于调用了pthread_create通常还需要在编译时调用pthread动态库。

多线程需要注意的点:

 1.在创建之前可以先用man查看pthread_create()细节

 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

 2.线程的创建并不能保证哪个线程会先运行

 3.在编译时,一定要调用系统自带的pthread动态库(-lpthread)

 4.如果创建失败,其多线程会返回一个错误码,并且是小于0的值。

 

实例:

创建一个多线程并打印每个线程对全局变量g_judge的调用:

Linux环境编程--多线程的工作原理以及多线程锁

Linux环境编程--多线程的工作原理以及多线程锁

程序运行结果:

Linux环境编程--多线程的工作原理以及多线程锁

思考:

 由于线程调度的顺序由系统来决定,因此,全局变量g_judge的调用赋值变化是不稳定的,换个意思是:A和B都同样调用g_judge这个全局变量,那么如果在A调用g_judge的同时B也在对g_judge调用,导致其值变化不按人们所想的值去变化。为了解决这个问题要求同一时间只允许一个线程访问改变量,所以引入锁,对数据进行保护。

 

互斥量(锁):

 可以使用pthread的互斥借口来保护数据,确保同一时间只有一个线程访问数据。

 互斥量使用pthread_mutex_t数据类型来表示。

 在使用之前,要对其进行初始化:

 PTHREAD_MUTEX_INITIALIZER(静态分配的互斥量)或pthread_mutex_init函数进行初始化。

 pthread_mutex_t                  lock;

 int pthread_mutex_init(pthread_mutex *restrict mutex, const pthread_mutexattr_t *restrict  atrr);

 int pthread_mutext_destroy(pthread_mutex_t *mutex);

 int pthread_mutex_lock(pthread_mutex_t *mutex);          // 阻塞

 int pthread_mutex_trylock(pthread_mutex_t *mutex);      // 非阻塞

 int pthread_mutex_unlock(pthread_mutex_t *mutex);      // 释放锁

避免死锁:

 如果线程试图尝试对同一个互斥量加锁两次,那么它自身会陷入死索的状态,以及其他不太明显的方式也能产生死锁。

 临界资源:所有程序都可调用的资源

 临界区:临界资源存放的位置

 如何解决死锁:通过仔细控制互斥量加锁的顺序(按顺序加锁)

 

对程序数据进行加锁处理:

 

 分别对workers1、workers2、workers3进行加锁其中,workers3对其进行上锁2次,进行死锁测试。

workers1:

Linux环境编程--多线程的工作原理以及多线程锁

workers2:

Linux环境编程--多线程的工作原理以及多线程锁

workers3:

Linux环境编程--多线程的工作原理以及多线程锁

测试结果:

 

 程序运行到workers3时,程序不在运行,证明了死锁的原因之一是重复上锁,导致解锁失败。

Linux环境编程--多线程的工作原理以及多线程锁

 如果屏蔽多余的上所步骤:

Linux环境编程--多线程的工作原理以及多线程锁

 保证了数据的规律性。