[Python 协程] 1. 为什么多线程很棘手

本文主要讨论 I/O 密集型任务,多线程自然适用于这个应用场景,因为它本身是为更高级的 CPU 密集型任务而设计的。然而多线程有很多明显的缺点,本文将通过简单的例子说明这一点,并解释为啥在用多线程时,我们常常(甚至是过多)使用线程锁。

线程和进程

在执行并行任务时,系统提供了两种工具:

  • 线程 threading
  • 进程 process

系统(operating System) 最重要的功能是让多个程序能同时运行,也就是说共享计算机的物理资源,尤其是 CPU 和内存,而不会互相影响。
通过底层构造,两个不同的进程在两个完全封闭的空间中运行,一个进程无法访问另一个进程的内存空间。但是使用多进程进行并行计算时,这种约束极大限制了其应用场景,因为很多时候不同的程序是相互依赖的,需要通信、共享数据等等。(固然存在一些内存共享库、通信库,但是它们本身又增加了程序的复杂性)。
一个进程中可以存在多个线程,每个线程有一个堆栈和一个程序指针,在线程内各指令顺序执行。多线程的好处是,它们共享其所属进程的内存。

调度器

线程和进程的运行由调度器完成,调度器有如下作用:

  • 维护一个需要运行的进程和线程的列表
  • 调度器以很高的频率轮流执行列表中各任务
  • 经过所有的编译和优化阶段,调度器操作的基本上是二进制代码,非常接近处理器。

上下文切换

调度器决定挂起某个正在执行的进程或者线程,并执行另一个任务时,称为上下文切换。从一个进程切转到另一个进程,称为进程切换,从一个线程转到另一个线程,称为任务切换或者线程切换
我们需要意识到一个重点是,调度器是一段通用代码,它以中立的方式来调度所有线程和进程,不受编程语言或者应用场景的影响,它分配给每个任务的时间基于处理器基本指令,也就是 周期。接下来我们主要关注线程,我们将会看到多线程可能带来的问题。

一个简单的加法运算

在单线程下,a=10,计算机执行 a=a+1 主要包含如下三步:

  • 从寄存器或缓存中读取变量 a 的值。
  • 计算加法
  • 将加法结果保存到之前 a 所在的内存空间。

在双线程下,计算两次 a=a+1,理想情况下是这样的:
两个线程顺序执行,第一个线程执行完 a 的值为 11,调度器执行上下文切换,进入第二个线程,重复上述三步,得到最终结果 12。
但实际上,调度器并不一定能恰好在线程1执行完后,才切换到线程2,它区分不出线程1和线程2是两个相关的线程,有别于其它线程。如果调度器选择在线程1的第1步,加载完变量 a 的值后,切换到线程2,那么线程2看到的 a 还是原始值,那么两个线程执行的结果就是11,而非12。

在稍微高级一点的编程语言中,一个代码片段总会被翻译成几条二进制指令,供处理器使用。为了使程序在多线程模式下正常运行,一些代码片段,尤其是访问共享内存的片段,必须要连续执行,而不能被调度器随意打断。如果没有程序员的特殊说明,调度器无法知道,在二进制指令流中,哪些片段是不能被中断的,哪些片段做上下文切换是合法的。因此,锁的概念被提出,使程序员可以显式指定哪些代码片段不能被打断,必须一次执行完。

# thread A
a = a+1
# thread B
a = a+1

被替换为:

# thread A
get_lock(lock)
a = a+1
release_lock(lock)
# thread B
get_lock(lock)
a = a+1
release_lock(lock)

上述代码中,一个新的全局对象 lock 被引入,它有两种状态:占用和释放。
两个线程中先执行的那个得到了锁,将其设置为占用状态,该线程直到执行完毕后,才会释放锁;另一个线程必须要等到第一个线程完成后(锁处于释放状态),才会被执行。

总结

尽管我们可以使用多线程写出运行正常的程序,但代价是引入额外的代码,使问题复杂化。之所以会产生这种问题,主要是程序员对上下文切换没有控制权,而上下文切换导致的问题可能发生在多线程程序运行的任何时刻。在接下来的文章中,我们会看到 async/await.asyncio 范式是如何解决多线程带来的问题的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容