美文网首页Python自动化运维
python 多进程 多线程 协程

python 多进程 多线程 协程

作者: meetliuxin | 来源:发表于2018-07-02 18:06 被阅读15次

    一、多进程

    1、子进程(subprocess包)

    在python中,通过subprocess包,fork一个子进程,并运行外部程序。

    import subprocess
    child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
    child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
    out = child2.communicate()
    print(out)
    

    subprocess参考
    从subprocess运行的子进程中实时获取输出

    2、多进程(multiprocessing包)

    它可以利用multiprocessing.Process对象来创建一个进程。

    进程池 (Process Pool)可以创建多个进程。
    apply_async(func,args) 从进程池中取出一个进程执行func,args为func的参数。它将返回一个AsyncResult的对象,你可以对该对象调用get()方法以获得结果。

    close() 进程池不再创建新的进程

    join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。

    join() wait进程池中的全部进程。必须对Pool先调用close()方法才能join。

    #! /usr/bin/env python
    # -*- coding:utf-8   -*-
    # __author__ == "111"
    # "我的电脑有4个cpu"
    
    from multiprocessing import Pool
    import os, time
    
    def long_time_task(name):
        print 'Run task %s (%s)...' % (name, os.getpid())
        start = time.time()
        time.sleep(3)
        end = time.time()
        print 'Task %s runs %0.2f seconds.' % (name, (end - start))
    
    if __name__=='__main__':
        print 'Parent process %s.' % os.getpid()
        p = Pool()
        for i in range(4):
            p.apply_async(long_time_task, args=(i,))
        print 'Waiting for all subprocesses done...'
        p.close()
        p.join()
        print 'All subprocesses done.'
    
    多进程共享资源

    通过共享内存和Manager对象:用一个进程作为服务器,建立Manager来真正存放资源。
    其它的进程可以通过参数传递或者根据地址来访问Manager,建立连接后,操作服务器上的资源。

    #! /usr/bin/env python
    # -*- coding:utf-8   -*-
    # __author__ == "111"
    
    from multiprocessing import Queue,Pool
    import multiprocessing,time,random
    
    def write(q):
    
        for value in  ['A','B','C','D']:
            print "Put %s to Queue!" % value
            q.put(value)
            time.sleep(random.random())
    
    
    def read(q,lock):
        while True:
            lock.acquire()
            if not q.empty():
                value=q.get(True)
                print "Get %s from Queue" % value
                time.sleep(random.random())
            else:
                break
            lock.release()
    
    if __name__ == "__main__":
        manager=multiprocessing.Manager()
        q=manager.Queue()
        p=Pool()
        lock=manager.Lock()
        pw=p.apply_async(write,args=(q,))
        pr=p.apply_async(read,args=(q,lock))
        p.close()
        p.join()
        print ("所有数据都写入并且读完")
    

    二、多线程

    threading包

    import time, threading
    
    # 新线程执行的代码:
    def loop():
        print('thread %s is running...' % threading.current_thread().name)
        n = 0
        while n < 5:
            n = n + 1
            print('thread %s >>> %s' % (threading.current_thread().name, n))
            time.sleep(1)
        print('thread %s ended.' % threading.current_thread().name)
    
    print('thread %s is running...' % threading.current_thread().name)
    t = threading.Thread(target=loop, name='LoopThread')
    t.start()
    t.join()
    print('thread %s ended.' % threading.current_thread().name)
    

    python的多线程在同一时刻只会有一条线程跑在CPU里面,其他线程都在睡觉。这个就是因为传说中的GIL(全局解释锁)的存在。任何Python线程执行前,必须先获得GIL锁.

    那python多线程还有用处吗?当然!

    如果是一个计算为主的程序(专业一点称为CPU密集型程序),这一点确实是比较吃亏的,每个线程运行一遍,就相当于单线程在跑,甚至比单线程还要慢——CPU切换线程的上下文也是要有开销的。

    辣鸡

    如果是一个磁盘或网络为主的程序(IO密集型)就不同了。一个线程处在IO等待的时候,另一个线程还可以在CPU里面跑,有时候CPU闲着没事干,所有的线程都在等着IO,这时候他们就是同时的了,而单线程的话此时还是在一个一个等待的。我们都知道IO的速度比起CPU来是慢到令人发指的,python的多线程就在这时候发挥作用了。比方说多线程网络传输,多线程往不同的目录写文件,等等。

    线程同步

    多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了.

    一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁。

    import time, threading
    
    # 假定这是你的银行存款:
    balance = 0
    
    def change_it(n):
        # 先存后取,结果应该为0:
        global balance
        balance = balance + n
        balance = balance - n
    
    def run_thread(n):
        for i in range(100000):
            change_it(n)
    
    t1 = threading.Thread(target=run_thread, args=(5,))
    t2 = threading.Thread(target=run_thread, args=(8,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(balance)
    

    解决:加锁

    balance = 0
    lock = threading.Lock()
    
    def run_thread(n):
        for i in range(100000):
            # 先要获取锁:
            lock.acquire()
            try:
                # 放心地改吧:
                change_it(n)
            finally:
                # 改完了一定要释放锁:
                lock.release()
    

    三、协程

    协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。

    线程是系统级别的,它们是由操作系统调度;协程是程序级别的,由程序员根据需要自己调度。我们把一个线程中的一个个函数叫做子程序,那么子程序在执行过程中可以中断去执行别的子程序;别的子程序也可以中断回来继续执行之前的子程序,这就是协程。也就是说同一线程下的一段代码<1>执行着执行着就可以中断,然后跳去执行另一段代码,当再次回来执行代码块<1>的时候,接着从之前中断的地方开始执行。

    协程的优点:

    (1)无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力)

    (2)无需原子操作锁定及同步的开销

    (3)方便切换控制流,简化编程模型

    (4)高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

    协程的缺点:

    (1)无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

    (2)进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

    参考:
    https://www.cnblogs.com/zingp/p/5911537.html
    https://blog.csdn.net/andybegin/article/details/77884645

    四、异步

    无论是线程还是进程,使用的都是同步进制,当发生阻塞时,性能会大幅度降低,无法充分利用CPU潜力,浪费硬件投资,更重要造成软件模块的铁板化,紧耦合,无法切割,不利于日后扩展和变化。

    不管是进程还是线程,每次阻塞、切换都需要陷入系统调用(system call),先让CPU跑操作系统的调度程序,然后再由调度程序决定该跑哪一个进程(线程)。多个线程之间在一些访问互斥的代码时还需要加上锁,

    现下流行的异步server都是基于事件驱动的(如nginx)。

    异步事件驱动模型中,把会导致阻塞的操作转化为一个异步操作,主线程负责发起这个异步操作,并处理这个异步操作的结果。由于所有阻塞的操作都转化为异步操作,理论上主线程的大部分时间都是在处理实际的计算任务,少了多线程的调度时间,所以这种模型的性能通常会比较好。

    参考:https://www.cnblogs.com/tyomcat/p/5486827.html

    相关文章

      网友评论

        本文标题:python 多进程 多线程 协程

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