• 并发和并行 | Python中实现多线程 threading 和多进程 multiprocessing


    并发和并行 | Python中实现多线程 threading 和多进程 multiprocessing

    昨天晚上组会轮到我汇报技术内容,最近正在和 ray 以及 spark 打交道,索性讲一下并发和并行。反正大家都是管理学院的,平时很少接触这种,因此这个选题不大可能因为内容基础而贻笑大方。

    本文摆一摆并发和并行。附上很简单的 Python 代码,涉及到自带库 threadingmultiprocessing 的使用。

    并发和并行

    咱们简单用多线程对应并发,多进程对应并行。多线程并发更强调充分利用性能;多进程并行更强调提升性能上限。

    我用非常简单且不那么严谨的比喻来说明。

    多线程

    一个 CPU 相当于一个学生。

    一个学生一周开一次组会,换句话说一周给老师汇报一次工作。

    老师一般会给学生同时布置几个任务,比如做比赛、做项目、读论文,学生可能周一做做比赛、周二读读论文、周三做做项目... 到了组会,他就把三件事都拿出来汇报,老师很欣慰,因为在老师的视角里:学生这三件事是同时在做的。

    多线程也是同一个道理,假设你的手机只有一块单核 CPU 。你的 CPU 这 0.01 秒用来播放音乐,下 0.01 秒用来解析网页... 在你的视角里:播放音乐和解析网页是同时进行的。你大可以畅快地边听音乐边网上冲浪

    何谓充分利用性能? 如果这学生只有一项工作,那他这一周可能只需要花费两天来做任务,剩下时间摸鱼(针不搓,三点钟饮茶先!)。因此,我们用「多线程」来让学生实现『并发』,充分利用学生能力。

    giphy.com

    在实际情况中,多线程、高并发这些词语更多地出现在服务端程序里。比如一个网络连接由一个线程负责,一块 CPU 可以负责处理多个异步的请求,大大提升了 CPU 利用率。

    多进程

    多个 CPU ( CPU 的多核)相当于多个学生。

    一个任务可以拆成几个任务相互协作、同时进行,则是多进程。

    比如研究生课程,老师非得留个论文作业,都研究生了我去,留啥大作业。

    那咱就多线程并行搞呗。确定了大概思路,剩下的一股脑写就行。咱队伍里一共甲乙丙丁四名同学,那就:

    • 甲同学负责 Introduction
    • 乙同学负责 Background
    • 丙同学负责 Related Works
    • 丁同学负责 Methodology

    这是乙同学提出异议:不应该是先完成 Introduction 再写 Background ,一个个来嘛?

    大哥,都研究生了嗷,作业糊弄糊弄差不多得了啊。让你写你就写。

    可以预知,上述四部分同时进行,怎么也比一个人写四块要快。

    giphy.com

    所以说 多进程并行提升性能上限

    在实际情况中,多进程更多地与高性能计算、分布式计算联系在一起。

    Python 实现

    首先声明咱的实验环境。

    > python --version
    Python 3.8.5
    

    咱们设置个任务:求数的欧拉函数值。

    def euler_func(n: int) -> int:
        res = n
        i = 2
        while i <= n // i:
            if n % i == 0:
                res = res // i * (i - 1)
                while (n % i == 0): n = n // i
            i += 1
        if n > 1:
            res = res // n * (n - 1)
        return res
    

    求一个数的欧拉函数值可能很快,但是一堆数呢?

    所以咱想着用并行完成这个任务。

    咱们把任务分成三份。

    task1 = list(range(2, 50000, 3))  # 2, 5, ...
    task2 = list(range(3, 50000, 3))  # 3, 6, ...
    task3 = list(range(4, 50000, 3))  # 4, 7, ...
    
    def job(task: List):
        for t in task:
            euler_func(t)
    

    来看看平平无奇的正常串行。

    @timer
    def normal():
        job(task1)
        job(task2)
        job(task3)
    

    完成了 task1 再完成 task2 ... 行,没毛病。

    看看多线程?

    import threading as th
    
    @timer
    def mutlthread():
        th1 = th.Thread(target=job, args=(task1, ))
        th2 = th.Thread(target=job, args=(task2, ))
        th3 = th.Thread(target=job, args=(task3, ))
    
        th1.start()
        th2.start()
        th3.start()
    
        th1.join()
        th2.join()
        th3.join()
    

    再看看多进程?

    import multiprocessing as mp
    
    @timer
    def multcore():
        p1 = mp.Process(target=job, args=(task1, ))
        p2 = mp.Process(target=job, args=(task2, ))
        p3 = mp.Process(target=job, args=(task3, ))
    
        p1.start()
        p2.start()
        p3.start()
    
        p1.join()
        p2.join()
        p3.join()
    

    上述代码的逻辑是这样的:

    • 我创建线程/进程,其生来的目的就是完成任务job(task1)job(task2)job(task3),注意这里函数名和参数被分开了target=job, args=(task1, )
    • 然后 start() ,告诉线程/进程:你可以开始干活了
    • 他们自己干自己的,咱们程序主逻辑还得继续往下运行
    • join() 这里,咱们是指让线程/进程阻塞住咱的主逻辑,比如p1.join()是指:p1不干完活,我主逻辑不往下进行(属于是「阻塞」)
    • 这样,我们的函数multcore结束后,一定其中的线程/进程任务都完成了

    咱看看结果:

    if __name__ == '__main__':
    
        print("同步串行:")
        normal()
    
        print("多线程并发:")
        mutlthread()
    
        print("多进程并行:")
        multcore()
    
    # 下面是结果
    同步串行:
    timer: using 0.24116 s
    多线程并发:
    timer: using 0.24688 s
    多进程并行:
    timer: using 0.13791 s
    

    结果不太对,按理说,多进程并行的耗时应该是同步串行的三分之一,毕竟三个同等体量的任务在同时进行。

    多线程并发同步串行慢是应该的,因为多线程并发同步串行的算力是一样的,但是多线程并发得在各个任务间来回切换,导致更慢。

    你问 @timer 是什么意思?哦,这个是我写的修饰器,如下。

    def timer(func):
        @wraps(func)
        def inner_func():
            t = time.time()
            rts = func()
            print(f"timer: using {time.time() - t :.5f} s")
            return rts
        return inner_func
    

    不太明白『Python修饰器』的老铁,不如给我点个「在看」,再关注下我,咱们以后详细道来。

    我是小拍,微信 PiperLHJ ,感谢关注与在看。

  • 相关阅读:
    《影响力》为你剖析营销的魅力 伍卓钧
    教你如何掌控别人 伍卓钧
    针对面向对象接口最诡异的解读 伍卓钧
    2011年终总结 伍卓钧
    打造阅读Linux源代码利器 伍卓钧
    系统运维的那些事文件权限 伍卓钧
    风扇控制系统最终版 伍卓钧
    码农小手册1 伍卓钧
    码农充电站进程与线程 伍卓钧
    面霸不容易且面且珍惜 伍卓钧
  • 原文地址:https://www.cnblogs.com/piperliu/p/14872955.html
Copyright © 2020-2023  润新知