如何提高python效率?——并发编程介绍

4,513次阅读
没有评论

共计 3169 个字符,预计需要花费 8 分钟才能阅读完成。

对于计算机而言,提高效率的方式其实是比较有限的,因为除非从底层代码(汇编代码)进行优化,否则想要提升效率是比较困难的,只能通过不断的优化代码和算法,提高效率。但实际上还有另外的方法也可以提高效率,来看看 python 有哪些提高效率的方法:

基础概念介绍

进程:进程是一个程序的执行实例,他是一个动态的概念,是操作系统进行资源分配的基本(最小)单位,一个进程中包含了程序执行过程中的所有资源。进程之间数据交换需要使用到中间件。

线程:线程是 CPU 的最小调度单位,同一个进程里面的线程共享所有资源(没错,一个进程里可以有多个线程,每个线程都运行在同一进程的上下文中,共享同样的代码和全局数据,所以线程间的数据交换会来得容易些)。

协程:由于 python 存在 GIL 锁,似的 python 的多线程没有太多意义,这时候出现了自己操作线程的切换,以降低线程切换的开销,这就是协程。

 通俗理解线程进程的关系:

进程是连锁店,线程是店里的灶台

一个进程可以有多个线程,——》一个连锁店可以有多个灶台

进程间不能直接通信——》连锁店之间通信是不方便的

线程间共享所有支援,可以直接通信——》灶台之间共享店内的所有食材
进程要比线程消耗更多的计算机资源。——》开个店比开个灶台更贵

提升效率的方法一——多进程

在上文的类比中,我们可以知道进程是连锁店,为了赚大钱(提高效率),我们可以多开几家店,这就是多进程技术。

在 python 中多进程使用 multiprocessing 库来实现。示例如下所示:


import multiprocessing

def worker1(name):
    print("worker1 name is" + name)

def worker2(name):
    print('worker2 name is' + name )


if __name__ == "__main__":
    
    
    p1 = multiprocessing.Process(target=worker1, args=('subprocess1',)) 
    p2 = multiprocessing.Process(target=worker2, args=('subprocess2'))

    
    p1.start()
    p2.start()
    
    
    p1.join()  
    p2.join()

还记得上文提到的嘛?多进程间进程通信是比较困难的,但这并不代表没有通信的手段,为了实现多进程间的通信,我们可以使用队列(Queue)来实现,它是一种多进程安全的队列,有关他的更多用法可以查看 python3 教程中的相关文档。

 为什么要使用队列来进行通信?很简单,因为如果没有通信,进程之间就无法协作,会出现冲突,就像开连锁店一样,如果没有协调好每个店的经营内容,可能会出现互相抢客户的现象。

提升效率的方法二——多线程

多线程技术在其他语言中是可以正常使用的,但在 python 中有例外,因为 python 存在一个 GIL 锁,它规定了线程在运行时,需要先拿到通行证,否则就不能运行,也就意味着一个 python 的进程里,无论你有多少个线程,永远只能单线程运行。

还记得上文说过,线程是 cpu 最小调度单位吗?也就意味着,python 多线程是无法使用多核的,但是多进程是可以利用多核的。

 怎么理解 GIL 锁呢,其实就是相当于只有一个大师傅,虽然你有很多灶台,但是你只有一个人可以做菜。

那么 python 的多线程是不是没用呢?不是,如果你的程序是 CPU 密集型的,那么 python 的多线程是完全没有意义,甚至由于线程切换的花销,会导致更慢点。

但如果你的是 IO 密集型,那么多线程的提升还是很明显的。

 io 密集型,就是读取数据比较耗费时间,而 cpu 处理时间比较短,程序花费的空闲的时间主要是 cpu 在等待 io,这就是 io 密集型,比如等待网络数据,文件读写等

CPU 密集型,就是处理数据比较耗费时间,读写不耗费时间,程序花费的时间主要是 cpu 在处理数据,而只有一小段时间是用在 io 上,这就是 cpu 密集型,比如算法运算,复杂逻辑处理等等。

以大师傅为例,io 密集型说的就是食材的烹饪前处理、端菜上桌比较耗费时间,cpu 密集型说的就是食材做成才比较耗费时间。

虽然大师傅可以有很多个灶台,但大师傅只有一个,cpu 密集型的程序就相当于大师傅要一直做菜,还要从一个灶台跑到另一个灶台,大师傅会很累,而且同一时间内大师傅只能在一个灶台上做菜,所以实际上也没有更快,因为大师傅还要跑来跑去,反而更慢了
io 密集型的程序就相当于大师傅做刺身(很简单的料理,但是提前处理材料很麻烦),每个灶台都有自动处理机器,它可以自动把食材处理好,只要大师傅到位就可以做菜,做完也可以不用管,马上切换到别的灶台继续做。所以多线程对于 io 密集型的程序提升确实是比较明显的。

python 的多线程使用的是 threading 库(其实还有 thread 库,但这个库比较简单,不推荐),示例如下所示:


import threading


def run(n):
    print("current task:", n)

if __name__ == "__main__":
    
    t1 = threading.Thread(target=run, args=("thread 1",))
    t2 = threading.Thread(target=run, args=("thread 2",))
    t1.start()
    t2.start()

协程介绍

协程是一种操作,原来多线程是由 CPU 控制的,而协程则是自己控制。当代码中出现有 io 处理的时候,先代码自行调度,将这个操作挂起,然后去继续执行其他操作。

这样的话,cpu 就不会因为代码中出现 io 处理进行线程切换,从而减少线程切换的花销,提升运行速度。

 大师傅在做菜的时候可能需要蒸十五分钟,这十五分钟大师傅完全可以去干别的,按照原来的多线程,大师傅得把这个灶头的菜坐完再切换到别的灶头,而协程的出现则改变了这个情况,大师傅发现蒸菜十五分钟,他就去别的灶台干别的活了,等到蒸好了再切换回来。

python 的协程使用的 asyncio 库,示例代码如下所示:

import asyncio
 
queue =  asyncio.Queue()

async def Producer():
        n = 0
        while True:
            await asyncio.sleep(2)
            print('add value to queue:',str(n))
            await queue.put(n)
            n = n + 1

async def Consumer():
    while True:
        try:
            r = await  asyncio.wait_for(queue.get(), timeout=1.0)
            print('consumer value>>>>>>>>>>>>>>>>>>', r)
        except asyncio.TimeoutError:
            print('get value timeout')
            continue
        except:
            break
    print('quit')
loop = asyncio.get_event_loop()
tasks = [Producer(), Consumer()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

协程跟进程、线程的区别

  1. 协程既不是进程也不是线程,协程仅仅是一个特殊的函数,协程它进程和进程不是一个维度的。
  2. 一个进程可以包含多个线程,一个线程可以包含多个协程。
  3. 一个线程内的多个协程虽然可以切换,但是多个协程是串行执行的,只能在一个线程内运行,没法利用 CPU 多核能力。
  4. 协程与进程一样,切换是存在上下文切换问题的。

小结

以上就是有关于 python 并发编程的简单介绍了,想要更多了解 python 的并发编程,可以前往裴帅帅老师的新课程——Python 多线程多进程多协程 并发编程实战 进行学习!

Python 多线程多进程多协程 并发编程实战

原文地址: 如何提高 python 效率?——并发编程介绍

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-09-23发表,共计3169字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)