美文网首页
为什么要学一学并发编程

为什么要学一学并发编程

作者: 润着 | 来源:发表于2017-11-15 16:33 被阅读55次

    世界上,很多事情在同时发生。

    咖啡厅里同时坐着很多人,门口的马路上同时有多辆车通过。而盯着屏幕的你,还听着耳机里迷人的音乐,写代码的同时,还浏览新闻。

    但不知道你有没有注意到,这里包含了两个“同时”。

    “多辆车同时通过”,无论把时间切到多细,一分钟、一秒钟,甚至一微秒,都有多辆车在动作。这称为“并行”。

    而“写代码的同时,浏览新闻”,当时间切到足够细时,只有一个动作在发生,在更长的长度上看,其实是两个动作交替进行。这称为“并发”。

    Rob Pike有个演讲《Concurrency Is Not Parallelism》,详细说明了并发和并行。简单而言,并发是指程序的逻辑结构。并发的程序有两个或以上逻辑控制流,如果把这些控制流画成时序流程图,它们在时间线上是可以重叠的。并行是指程序的运行状态:某一时刻被多个CPU流水线同时处理。显然,并行需要硬件支持。

    那么,不难理解:并发是并行的必要非充分条件。一个只有单一逻辑控制流的程序不可能被并行处理,而并发的程序在一个CPU流水线上执行也不是并行的。


    并发是对问题更本质的描述,现实世界很多事情是并发的,而编程是对现实世界的建模。我们希望写出的代码也能有亲切的并发感。

    实际上操作系统也是这么抽象的,“喝咖啡”、“写代码”、“看新闻”……这些完成特定动作的代码片段,称为任务。多个任务交替地在CPU中运行着,当然,多颗核心时,也可能并行地运行。

    要不要运行某个任务,什么时候运行,运行多久,操作系统回答这些问题的过程,称为调度。需要注意的是,操作系统调度的单位不是任务,而是线程。线程是操作系统对并发的实现。

    在同一个线程内的任务是严格按代码顺序执行的,只有多个线程上的任务才会交替执行。而我们写出的代码默认是在一个线程内的。这样一来,如果没有特别设计,代码是串行执行,而不是并发执行。

    比如,某段代码先执行A部分,并把A的结果打印出来,接着执行与A没有任何关系的B部分,示意如下:



    整体是一个任务,并按顺序一步步执行,总耗时是T(A)+T(print A) + T(B)。如果拆成两个任务,分别在各自线程中执行,示意如下:


    B不必等A的print结束再执行,哪怕只有一颗CPU核心。因为一个调度单位变成了两个,当其中一个不占用CPU时间(print)时,另一个就可能被执行。总耗时不超过任一任务的耗时。

    使用多个线程,让更多核心派上用场,提高了资源的利用率,加快了程序执行速度。同时还有助于获得更高的吞吐率。当某个线程在等待I/O操作完成,另一个线程可以继续运行,使得程序整体能在I/O阻塞时继续工作。好比在等公交车到站的同时看手机,而不是等上车后才开始看。

    了解并发的好处之后,也得听一听反对的声音,尤其是当这个声音来自于linux和git之父Linus时。2014年,Linus说道:

    Where the hell do you envision that those magical parallel algorithms would be used?

    The only place where parallelism matters is in graphics or on the server side, where we already largely have it. Pushing it anywhere else is just pointless.

    Trust me, concurrency is hard. There's a reason all the examples of "look how easy it is to parallellize things" tend to use simple arrays and don't ever have allocations or freeing of the objects.

    People who think that the future is highly parallel are invariably completely unaware of just how hard concurrency really is.

    并发编程并不是没有代价的,而且可以说有高昂的代价。虽然人可以同时做多件事,但不意味着擅长同时做多件事,也不代表同时做的效率更高。相反,人擅长的是串行模式,专注于一件事,做完之后再做另一件。

    并发程序的设计和实现异常复杂。如何恰当地切分功能就已经很考验人,再考虑到多线程的乱序执行,以及线程之间的协调需求,只要稍不留神,就会失之毫厘、谬以千里。开发、阅读和调试都相当困难,即使是实战多年的并发专家,也不敢掉以轻心。

    因此,需要更加审慎地对待并发技术,不盲目使用。先实现功能,再关心性能是否足够。

    The strategy is definitely: first make it work, then make it right, and, finally, make it fast.——Kent Beck

    当然,Linus也认可并发在显卡和服务器编程领域的必要性。对于前者,由于深度学习的全面流行,显卡厂商Nvidia股份一再创新高,其火爆程度可见一斑。为并发而生的Golang(语言级别的并发支持,简单的并发模型)在服务器端大行其道,也证明了Linus所言非虚。

    并发编程领域所关注的问题,主要包括:

    • 如何抽象与建模
    • 并发粒度的控制
    • 工作者的调度
    • 工作者间的协作

    通常,对这些问题的考虑只发生在同一程序内,甚至只是其中某一模块或功能点。但如果缩小一下放大镜,从更高的位置看全景图,则会发现并发编程和分布式编程颇有几分相似。并发系统内,同一进程内不同线程之间的关系,对应了分布式系统中,不同机器上部署的进程之间的关系。而线程和进程有很多相似之处,只不过分布式系统多了网络相关的故障。因此,对并发编程的学习和研究,对分布式系统架构的设计也有借鉴意义。

    REF.

    相关文章

      网友评论

          本文标题:为什么要学一学并发编程

          本文链接:https://www.haomeiwen.com/subject/kwchvxtx.html