一文了解Linux多线程,互斥锁、条件变量、自旋锁、信号量的含义

1.

罗小猪同学的时间管理高端课程已经开了有一阵子了,不知道大家有什么新的感悟没有。前阵子我的基友问了我一个问题,说Linux线程同步锁怎么理解。不知怎的,我脑海中瞬间浮现出了罗小猪同学的形象,于是有此一文。

2.

开始之前,先解释一下啥叫线程。用官方的话来说呢,进程是资源分配的最小单位,线程是CPU执行程序的最小单位。每当有面试官考你“进程和线程有什么区别的时候”,请你牢记上面那句话。

假如我们的世界是个操作系统,每个男生都是一段程序。一个男生出生了,这叫进程被创建了;他获得了聪明的大脑和灵活的身体,这叫系统为进程分配了资源。

春天来了,他春心浮动,跟一个女生谈恋爱了,这叫单进程单线程。

夏天来了,他燥热不已,跟多个女生同时谈恋爱了,这叫单进程多线程。

秋天来了,他寂寞难耐,约上多个兄弟和多个女生开展了一场多人运动,这叫多进程多线程。

冬天来了,他东窗事发,女朋友把他的好事公之于众了,这叫程序出bug了。

每个渣男的基本自我修养就是要能在多个女生之间周旋并且不被发现。真正道行高的渣男,都是会让自己的每个女朋友都以为他是全心全意陪她的,聊天都聊到凌晨三四点了,怎么还有空去找别的女生呢?殊不知,只要做好时间管理,一个人也可以变成三个人。

一文了解Linux多线程,互斥锁、条件变量、自旋锁、信号量的含义

一文了解Linux多线程,互斥锁、条件变量、自旋锁、信号量的含义

 

周小姐的这句话简直一语道破Linux多线程的真谛。

我们用多线程技术,一般都是由于有多个任务想要同时执行,或者不想让某个需要长时间等待的任务过多的占用CPU的时间。就好像渣男总想同时交往多个女朋友,并且不想让来姨妈的女朋友占用自己过多的时间一样。

但是渣男实际上从头到尾只有一个人,那么他是如何做到让多个女朋友都以为他是她的唯一呢?

秘诀当然就是时间管理。

对于单核CPU来说,同一时刻只能执行一个线程。就如同渣男同一时刻,只能陪一个女友一样。但是硬件不够,脑子来凑。

渣男可以把自己的时间分割成一小段一小段的,这一小段的时间在Linux系统中被称为“时间片”。第一段时间片给女友周小姐,下一段时间片给蝴蝶小姐,再下一段给张小姐,再下一段又给周小姐......这个过程被称为“时间片轮转”。只要时间片足够短(比如只有几微秒),轮转的足够快(比如每秒轮转上亿次),每个女友就好像完整的拥有了这个渣男一样。

把渣男换成CPU,把小姐们换成线程,这就是多线程的实现机制了。

听起来好像是很完美的机制,但是你们有没有想过这样一个问题,虽然罗同学有着十分高端的时间管理秘术,但是罗同学身上的资源是唯一的,一旦被多个女友同时操作就要出问题的,尤其是肉体上的。比如他的.......

脖子。

罗同学的脖子只有一个,假如在某个时间片上被蝴蝶小姐在脖子上种了一只草莓,那在下一个时间片上陪周小姐的时候,不是就被发现异常了吗?

一文了解Linux多线程,互斥锁、条件变量、自旋锁、信号量的含义

再比如周小姐和蝴蝶小姐都想往罗同学脖子上纹身,一个想纹“勒布朗詹姆斯”,一个想纹“科比布莱恩特”,结果由于时间片轮转机制,两个人相当于是同时操作,那最终不就纹成乱码了吗?

这妥妥的要被发现,然后发微博长文,然后分手。也就是上文说的出bug了。

所以为什么说线程锁和同步机制是每个渣男梦寐以求的技术呢?

因为他们又想多线程,又不想出bug。

Linux线程同步机制有互斥锁、自旋锁、条件变量、信号量等,可以从不同的角度保证罗同学安全落地不出bug。咱们一个一个说。

假如今天是情人节,罗同学的3个女友都要求今晚要亲亲抱抱举高高。那么为了不让她们撞见,罗同学采用了互斥锁的机制。

情人节当天,他给每个女友都送了一把锁当做礼物,并告知当她进卧室的时候把门锁上,离开的时候把锁打开拿掉。

所以当卧室没有人的时候,门没有锁。当某个女友A进来后,把卧室门加个锁锁住,并在和罗同学亲亲抱抱举高高完事后,把门锁打开拿走。如果在亲亲抱抱举高高的过程中,有另一个女友B来了,发现门被锁住了,则助理把女友B请到在客厅等待,一直等到女友A完事后把门锁打开了,助理再去客厅通知女友B进去。当然,女友B进去时也会把门上锁,防止女友C的闯入,直到完事离开时才会把锁打开。这就是互斥锁的机制。

使用互斥锁时要注意的是,在线程操作完后必须要把锁打开,否则就会造成“死锁”,让门外的女友们苦等到天亮甚至永远。

当然,不是每个女友都像女友B这样好脾气,发现男朋友家的门锁上了就乖乖去客厅等人通知的,有的女友就是会在门口不停的尝试加锁,直到这个门被解锁为止,这类女友带的锁被称为“自旋锁”。

从Linux系统实现原理上来讲,互斥锁属于sleep-waiting类型的锁,而自旋锁属于busy-waiting类型的锁。相信你从字面意思就能理解两者的区别。

虽然相对于互斥锁来说,自旋锁比较浪费CPU资源,但是自旋锁适用于线程持有锁时间比较短的情况。比如罗同学亲亲抱抱举高高的时间只有三秒的话,那确实不用去客厅啰嗦那一圈了,站门口等就行了。

当然,也不是每个女友都是为了和罗同学亲亲抱抱举高高而进来的,女友张小姐就喜欢一些更和谐的运动,比如......和罗同学一起打游戏。

一文了解Linux多线程,互斥锁、条件变量、自旋锁、信号量的含义

但是她排队加锁进来后才发现,罗同学家的游戏手柄找不到了,于是她打电话让跑腿小哥帮忙去5公里外的超市里买一对游戏手柄送来,为了不浪费大忙人罗同学的时间,她决定先把卧室解锁,然后去客厅沙发上睡会儿,等到跑腿小哥的游戏手柄送到罗同学的房间里了,再把她叫醒,然后加入排队的队伍中。

这个机制就叫做“条件变量”,对某些缺少资源的线程来说,可以把这些线程暂停挂起,当获取到资源时再讲线程唤醒继续执行。

使用条件变量时需要注意的是,必须依赖互斥锁使用,即必须给跑腿小哥和张小姐每人一把互斥锁,保证唤醒张小姐的操作不会丢失。

否则在时间片无规律的轮转中,有可能出现跑腿小哥送来游戏手柄后,女友张小姐却去沙发上睡觉的情况,这会造成线程“死锁”,张小姐永远等不来她的游戏手柄,因为手柄在她睡觉之前已经送来了,她却不知道。这种情况在Linux系统中被称作“唤醒丢失”。

当然,吃瓜群众最震惊的,还是罗同学的多人运动技能了。也就是说,对于普通人来说,也许一个房间只能进去一个女友,但是对罗同学来说,却可以进去多个。比如罗同学在情人节的夜晚想要打麻将,那么这个房间将可以同时容纳四个人。这个时候互斥锁就不适合拿来用了,此刻需要祭出我们的终极武器:信号量。

信号量本质上是一个计数器,有的翻译也称作信号灯,都是一个意思。以罗同学想要打麻将为例,他可以在门口点亮三个灯。当一个女友进来房间时,则灭掉其中一盏;当某个女友有事要离开时,则点亮一盏。

那么当女友们来到门前时,如果门口还有灯亮着,则表示允许进入。同时每进入一个女友,则灭掉一盏灯。直到所有的灯都灭了,则门外的剩下的女友只能等待。直到有一盏以上的灯重新亮起,表示房间里终于有了空位。这就是信号量的运行机制。

于是不管怎样,今晚罗同学始终都能保持和三个女友一起快乐的进行多人运动:

打一通宵麻将了。

好了以上就是本期的全部内容了,感谢你能看到这里。文章首发于公众号“李纳克斯Linux”,有更多职场技术分享,欢迎关注指教。本文引用罗小猪先生的案例,仅仅是为了使讲解的知识变得通俗直观,并不代表什么立场。不论事情的幕后真相究竟如何,在舞台上的人,没有人是傻子。

 

公众号:李纳克斯Linux,欢迎指教

一文了解Linux多线程,互斥锁、条件变量、自旋锁、信号量的含义