• day10--进程


        进程:

        Python 解释器有一个全局解释器锁(PIL),导致每个 Python 进程中最多同时运行一个线程,因此 Python 多线程程序并不能改善程序性能,不能发挥多核系统的优势,可以通过这篇文章了解。

        但是多进程程序不受此影响, Python 2.6 引入了 multiprocessing 来解决这个问题。这里介绍 multiprocessing 模块下的进程,进程同步,进程间通信和进程管理四个方面的内容。 这里主要讲解多进程的典型使用,multiprocessing 的 API 几乎是完复制了 threading 的API, 因此只需花少量的时间就可以熟悉 threading 编程了

        由于GIL的存在,很多Python专家建议,多使用多进程,以便利用多核资源,而不是使用多线程。

        下面来下一个简单生成线程的实例,如下:

    import multiprocessing
    import time
    
    def run(name):
        time.sleep(2)
        print("hello",name)
    
    if __name__ == "__main__":
        for i in range(5):
            p = multiprocessing.Process(target=run,args=("alex",))
            p.start()
    执行结果如下:
    hello alex
    hello alex
    hello alex
    hello alex
    hello alex

        上面一个程序就是多进程的实例,启动了5个进程,可见,多进程是multiprocessing模块;

        multiprocessing.cpu_count()统计CPU的个数,即同时可以运行几个进程。cpu_count()统计CPU的个数,即几核CPU。

        在Linux上,每一个进程都是由父进程启动的,根进程1,__init__()负责启动;父进程,每个进程必定有一个父进程。

        下面我们来写一个程序,来看看进程里面的一些概念:

    import multiprocessing,os,time
    
    def info(title):
        print(title)
        print("Module name:",__name__)
        print("parent processing:",os.getppid())                             #获取线程父线程的PID
        print("son processing:",os.getpid())                                 #获取线程的子线程PID
        print("
    ")
    
    def func(name):
        info("33[31m----------------函数func-------------------33[0m")    #启动线程之后调用info()函数
        print("name:",name)
    
    if __name__ == "__main__":
        info("33[32m---------------主程序的进程信息-------------33[0m")
        '''启动一个进程'''
        p = multiprocessing.Process(target=func,args=("geng",))
        p.start()
    执行结果:
    ---------------主程序的进程信息-------------
    Module name: __main__
    parent processing: 2601
    son processing: 6877


    ----------------函数func-------------------
    Module name: __main__
    parent processing: 6877
    son processing: 6878


    name: geng

        上面程序中,__name__是指模块名,os.getppid()是获取父进程的ID,os.getpid()获取自己的进程PID,我们知道,进程都有自己的进程号,PID,

        上面的进程PID:2601是父进程,启动了进程:6877,进程6877启动了6878,那么父进程2601是谁呢?这个进程是Pycharm的进程PID,程序的PID是由Pycharm进程启动的,在Linux上,每一个进程都是由父进程启动的。

        进程间通讯

        不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:

        我们知道,线程之间数据是可以共享的,如下:

    from multiprocessing import Process,Queue
    import queue,threading
    
    def func(mess):
        q.put(mess)
    
    if __name__ == "__main__":
        q = queue.Queue()
        '''定义一个线程'''
        t = threading.Thread(target=func,args=("geng",))
        t.start()
        print(q.get())
    执行结果:
    geng

        从上面看,线程之间是可以共享队列的数据的,下面我们来看下进程的情况:

    from multiprocessing import Process
    import queue,threading
    
    def func():
        q.put(["alex","tom"])
    
    if __name__ == "__main__":
        q = queue.Queue()
        '''定义一个线程'''
        p = Process(target=func)
        p.start()
        print(q.get())
    执行结果:
    Traceback (most recent call last):
      File "/home/zhuzhu/day10/进程数据共享.py", line 12, in <module>
        print(q.get())
      File "/usr/lib/python3.5/queue.py", line 164, in get
        self.not_empty.wait()
      File "/usr/lib/python3.5/threading.py", line 293, in wait
        waiter.acquire()
    KeyboardInterrupt

        可见,进程之间是不能随便通信的,要想实现进程之间的通信,要使用进程Queue。如下:

    from multiprocessing import Process,Queue
    
    def func():
        q.put(["alex","tom"])
    
    if __name__ == "__main__":
        q = Queue()
        '''定义一个线程'''
        p = Process(target=func)
        p.start()
        print(q.get())
    执行结果:
    ['alex', 'tom']

        可见,通过进程multiprocessing模块中间的进程Queue能够实现不同进程之间的通讯。进程之间的通信情况。

        上面程序中,有两个进程,父进程和子进程,父进程是程序本身,是由Pycharm启动的,子进程p是由程序本身的父进程启动的,父进程就是程序本身。两者之间是相互独立的,想要实现数据的交流,要通过进程Queue。

        Pipe(管道):管道函数返回一个由管默认为双相连接的连接对象(双向)

        通过管道之间实现数据的交换,管道有两头,连接两个线程,parent_conn,child_conn = Pipe(),就像socket一样,有客户端和服务器端,通过发送和接收数据,就能实现管道之间的数据交换,如parent_conn.send()和child_conn.recv(),并且管道之间可以接收和发送空的数据,都不会影响。如下:

    '''线程之间正常是不能共享数据的,线程是相互独立的,要想共享数据,要通过一些特殊的方法,下面通过Pipe管道实现数据共享'''
    '''Pipe实现数据共享是数据交换,与socket功能类似,一边发送数据,一边接收数据'''
    from multiprocessing import Process,Pipe
    
    def func(mess):
        child_conn.send(mess)
        print("子进程发送的数据:",mess)
      child_conn.close()
    if __name__ == "__main__": parent_conn,child_conn = Pipe() #建立管道,管道有两端,两个线程一边负责一端 while True: mess = input("请输入您要交父线程的内容>>:") p = Process(target=func,args=(mess,)) p.start() p.join() data = parent_conn.recv() print("父进程接收到的消息:",data)
    执行结果如下:
    请输入您要交父线程的内容>>:子进程发送数据,父进程接收数据
    子进程发送的数据: 子进程发送数据,父进程接收数据
    父进程接收到的消息: 子进程发送数据,父进程接收数据
    请输入您要交父线程的内容>>:管道有两端,通过两端管道的send,recv()实现数据的传输
    子进程发送的数据: 管道有两端,通过两端管道的send,recv()实现数据的传输
    父进程接收到的消息: 管道有两端,通过两端管道的send,recv()实现数据的传输
    请输入您要交父线程的内容>>:管道可以收发空的消息
    子进程发送的数据: 管道可以收发空的消息
    父进程接收到的消息: 管道可以收发空的消息
    请输入您要交父线程的内容>>:
    子进程发送的数据:
    父进程接收到的消息:
    请输入您要交父线程的内容>>:上面通过收发空消息并没有报错。
    子进程发送的数据: 上面通过收发空消息并没有报错。
    父进程接收到的消息: 上面通过收发空消息并没有报错。

        上面,我们通过管道,实现了两端数据的收发,Pipe(),有两端,parent_conn,child_conn = Pipe(),管道,当完成管道数据交换后,关闭管道,

    当Pipe()管道关闭之后,就不能交换数据了。

        管道Pipe()有两头,一边一个,两边负责收发各自的消息。

        Pipe的源代码如下:

    class Connection(object):
        def send(self, obj):
            pass
    
        def recv(self):
            pass
    
        def fileno(self):
            return 0
    
        def close(self):
            pass
    
        def poll(self, timeout=None):
            pass
    
        def send_bytes(self, buffer, offset=-1, size=-1):
            pass
    
        def recv_bytes(self, maxlength=-1):
            pass
    
        def recv_bytes_into(self, buffer, offset=-1):
            pass
    
        def __enter__(self):
            pass
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            pass
    
    
    def Pipe(duplex=True):
        return Connection(), Connection()

        Manager

        进程之间相互独立,Manager默认自己会加锁,不用额外加锁,担心两个线程同时修改数据,这种事情不会发生,进程操作完了另外一个进程才会操作。

        上面,我们通过Pipe()管道,Queue队列实现了线程之间的数据交换,但是也只是限于传递,各个线程之间是不能修改数据的,那么如何修改数据呢?我们可以通过多线程里面的Manager来修改线程之间的数据。如下:

    """之前都是共享数据,并不能在一个线程修改另外一个线程的数据,下面通过Manager来修改数据"""
    from multiprocessing import Process,Manager
    import os
    
    def func(d,l):
        '''把Manger的生成的字典和列表实例当做参数传入,并修改'''
        d[os.getpid()] = os.getppid()
        l.append(os.getpid())
        print("子线程的列表:",l)
    
    if __name__ == "__main__":
        with Manager() as manager:                                #使用with定义
            d = manager.dict()                                    #生成一个字典,可在多个进程间共享和传递
            l = manager.list()                                    #定义一个Manager列表
    
            p_lists = []                                              #定义一个线程空的字典,存放启动的线程,稍后遍历,确保都执行完毕
            for k in range(10):
                p = Process(target=func,args=(d,l))
                p.start()
                p_lists.append(p)
    
            for process in p_lists:
                process.join()
    
            print("字典:",d)
            print("列表:",l)
    代码执行结果:
    子线程的列表: [9717]
    子线程的列表: [9717, 9720]
    子线程的列表: [9717, 9720, 9718]
    子线程的列表: [9717, 9720, 9718, 9722]
    子线程的列表: [9717, 9720, 9718, 9722, 9727]
    子线程的列表: [9717, 9720, 9718, 9722, 9727, 9725]
    子线程的列表: [9717, 9720, 9718, 9722, 9727, 9725, 9733]
    子线程的列表: [9717, 9720, 9718, 9722, 9727, 9725, 9733, 9734]
    子线程的列表: [9717, 9720, 9718, 9722, 9727, 9725, 9733, 9734, 9731]
    子线程的列表: [9717, 9720, 9718, 9722, 9727, 9725, 9733, 9734, 9731, 9736]
    字典: {9736: 9708, 9734: 9708, 9731: 9708, 9717: 9708, 9718: 9708, 9720: 9708, 9722: 9708, 9733: 9708, 9725: 9708, 9727: 9708}

        上面代码中,我们使用了Manager()来定义了字典和列表,通过Manager()定义的字典和列表是可以修改的,从结果可以看出,修改成功,但是所有主程序的代码都要在with Manager() as manager:后面,不然会报错。

        如果不想注意缩进,即with Manager() as manager:所有语句缩进后面,可以通过赋值,manager = Manager()也可以,这样就能在同一个缩进里面执行程序,如下:

    """之前都是共享数据,并不能在一个线程修改另外一个线程的数据,下面通过Manager来修改数据"""
    from multiprocessing import Process,Manager
    import os
    
    def func(d,l):
        '''把Manger的生成的字典和列表实例当做参数传入,并修改'''
        d[os.getpid()] = os.getppid()
        l.append(os.getpid())
        print("子线程的列表:",l)
    
    if __name__ == "__main__":
        manager = Manager()                                   #使用with定义
        d = manager.dict()                                    #生成一个字典,可在多个进程间共享和传递
        l = manager.list()                                    #定义一个Manager列表
    
        p_lists = []                                          #定义一个线程空的字典,存放启动的线程,稍后遍历,确保都执行完毕
        for k in range(10):
            p = Process(target=func,args=(d,l))
            p.start()
            p_lists.append(p)
    
        for process in p_lists:
            process.join()
    
        print("字典:",d)
        print("列表:",l)
    执行结果如下:
    子线程的列表: [10295]
    子线程的列表: [10295, 10297]
    子线程的列表: [10295, 10297, 10296, 10300, 10299]
    子线程的列表: [10295, 10297, 10296, 10300, 10299]
    子线程的列表: [10295, 10297, 10296, 10300, 10299]
    子线程的列表: [10295, 10297, 10296, 10300, 10299, 10305, 10302]
    子线程的列表: [10295, 10297, 10296, 10300, 10299, 10305, 10302]
    子线程的列表: [10295, 10297, 10296, 10300, 10299, 10305, 10302, 10309, 10307]
    子线程的列表: [10295, 10297, 10296, 10300, 10299, 10305, 10302, 10309, 10307, 10303]
    子线程的列表: [10295, 10297, 10296, 10300, 10299, 10305, 10302, 10309, 10307, 10303]
    字典: {10305: 10286, 10307: 10286, 10309: 10286, 10295: 10286, 10296: 10286, 10297: 10286, 10299: 10286, 10300: 10286, 10302: 10286, 10303: 10286}
    列表: [10295, 10297, 10296, 10300, 10299, 10305, 10302, 10309, 10307, 10303]

        上面使用的就是manager = Manager(),这样我们就不必过分担心缩进的问题了。

        进程同步

        如果不使用来自不同进程的锁输出,就很容易混淆起来。(Without using the lock output from the different processes is liable to get all mixed up.

        进程之间是相互独立的,进程里面为什么需要锁呢?屏幕共享,锁存在的意义,由于屏幕共享,如果同时打印,就会出现错误,加锁的目的是只允许一个程序打印。

    from multiprocessing import Process,Lock
    
    '''进程是相互独立的,但是由于打印的时候屏幕是共享的,加锁的意义就是让一个进程打印的时候,其他进程暂时不打印,避免抢屏幕'''
    def func(mess):
        lock.acquire()
        try:
            print("33[31m打印用户输入:%s33[0m" %mess)
        finally:
            lock.release()                                     #finally()不管是否有异常,都执行
    
    
    if __name__ == "__main__":
        lock = Lock()
        mess = input("请输入您要让子线程打印的消息>>:")
        for i in range(10):
            p = Process(target=func,args=(mess,))
            p.start()
    执行结果:
    请输入您要让子线程打印的消息>>:进程之间加锁的目的是防止屏幕打印错乱
    打印用户输入:进程之间加锁的目的是防止屏幕打印错乱
    打印用户输入:进程之间加锁的目的是防止屏幕打印错乱
    打印用户输入:进程之间加锁的目的是防止屏幕打印错乱
    打印用户输入:进程之间加锁的目的是防止屏幕打印错乱
    打印用户输入:进程之间加锁的目的是防止屏幕打印错乱
    打印用户输入:进程之间加锁的目的是防止屏幕打印错乱
    打印用户输入:进程之间加锁的目的是防止屏幕打印错乱
    打印用户输入:进程之间加锁的目的是防止屏幕打印错乱
    打印用户输入:进程之间加锁的目的是防止屏幕打印错乱
    打印用户输入:进程之间加锁的目的是防止屏幕打印错乱

        上面程序就是加锁的情况,加锁是为了避免屏幕打印错乱,有时候多进程同时打印会出现打印错乱,由于屏幕共享,不知道其他线程什么时候执行完毕。

        进程池

        进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。启一个进程等于克隆一个父进程,CPU开销非常大,在Windows启动100个进程都很慢。启一个进程就相当于克隆一个父进程的内存数据。

        进程池中有两个方法:

        1.apply          (串行)

        2.apply_async    (asynchronous异步)

        apply方法的定义:

        def apply(self, func, args=(), kwds={}):
        '''
        Equivalent of `func(*args, **kwds)`.
        '''
        assert self._state == RUN
        return self.apply_async(func, args, kwds).get()

        首先使用apply()方法,写一个串行的进程池,当然这种方法很少见,不怎么常用,如下:

    from multiprocessing import Pool
    import time,os
    '''进程池,控制同一时间执行进程的个数,防止CPU运行吃力,启用进程很耗费CPU'''
    
    def fun():
        print("33[31min the process of %s33[0m" %os.getpid())
        time.sleep(1)
        print("进程%s执行完毕" %os.getpid())
    
    def bar():
        '''当一个子线程执行完毕之后,父线程调用回调函数,比如公司发完工资之后,会发一条短信到你手机,这样就可以用回调函数'''
        print("您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!")
    
    if __name__ == "__main__":
        pool = Pool(processes=5)                                           #定义同一时间最大运行进程的个数
        for i in range(10):
            pool.apply(func=fun)
    
        print("The end!")
        pool.close()
        pool.join()
    执行结果:
    in the process of 13326
    进程13326执行完毕
    in the process of 13327
    进程13327执行完毕
    in the process of 13329
    进程13329执行完毕
    in the process of 13330
    进程13330执行完毕
    in the process of 13328
    进程13328执行完毕
    in the process of 13326
    进程13326执行完毕
    in the process of 13327
    进程13327执行完毕
    in the process of 13329
    进程13329执行完毕
    in the process of 13330
    进程13330执行完毕
    in the process of 13328
    进程13328执行完毕
    The end!

        上面apply()是执行的串行进程,当一个进程执行完毕,才会执行另外一个,这样就失去了并发的意义,要想执行并发,就要使用apply_async并发执行。

        apply_async方法的定义:

        def apply_async(self, func, args=(), kwds={}, callback=None,
      error_callback=None):
        '''
        Asynchronous version of `apply()` method.
        '''
        if self._state != RUN:
        raise ValueError("Pool not running")
        result = ApplyResult(self._cache, callback, error_callback)
        self._taskqueue.put(([(result._job, None, func, args, kwds)], None))
        return result

        上面的程序是串行的,现在我们修改一下,让进程多并发执行(即异步),如下:

    from multiprocessing import Pool
    import time,os
    '''进程池,控制同一时间执行进程的个数,防止CPU运行吃力,启用进程很耗费CPU'''
    
    def fun():
        print("33[32m-------------开始打印-------------------33[0m")
        print("33[31min the process of %s33[0m" %os.getpid())
        time.sleep(1)
        print("进程%s执行完毕" %os.getpid())
    
    def bar():
        '''当一个子线程执行完毕之后,父线程调用回调函数,比如公司发完工资之后,会发一条短信到你手机,这样就可以用回调函数'''
        print("您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!")
    
    if __name__ == "__main__":
        pool = Pool(processes=5)                                           #定义同一时间最大运行进程的个数
        for i in range(10):
            # pool.apply(func=fun)
            pool.apply_async(func=fun)
    
        print("The end!")
        pool.close()
        pool.join()
    执行结果如下:
    The end!
    -------------开始打印-------------------
    in the process of 13562
    -------------开始打印-------------------
    in the process of 13563
    -------------开始打印-------------------
    in the process of 13564
    -------------开始打印-------------------
    -------------开始打印-------------------
    in the process of 13565
    in the process of 13566
    进程13563执行完毕
    进程13562执行完毕
    进程13564执行完毕
    进程13566执行完毕
    进程13565执行完毕
    -------------开始打印-------------------
    in the process of 13563
    -------------开始打印-------------------
    in the process of 13566
    -------------开始打印-------------------
    in the process of 13564
    -------------开始打印-------------------
    in the process of 13565
    -------------开始打印-------------------
    in the process of 13562
    进程13563执行完毕
    进程13565执行完毕
    进程13566执行完毕
    进程13562执行完毕
    进程13564执行完毕

        可以看出,上面执行的结果是5个异步同时执行的,只有前5个进程执行完毕,才会继续执行,pool.apply_async()多并发异步执行,但是可以看出,上面打印是按照谁先执行就先打印到屏幕上的,有点错乱,当然这是多进程正常的情况,并且要注意,进程池中,是先pool.close()关闭进程池,然后join()的,这个跟断言有关,具体可以看源代码,但是一定要记住,必须有pool.join()不然会报错,打印不了,这个是个坑。

        回调,我们经常会遇到这样的问题?就是做完一件事情之后,想备注一下,或者告知一下,比如,公司发完公司,总会通过银行发送一条消息到员工的手机上,如何操作呢?就可以用到进程池中的回调,如下:

        callback=func(函数名),只需要在apply_async(func=函数名,args=(参数),callback=回调函数名)就能实现回调,如下,我们调用完线程之后,发送一条消息,如下:

    from multiprocessing import Pool,Lock
    import time,os
    '''进程池,控制同一时间执行进程的个数,防止CPU运行吃力,启用进程很耗费CPU'''
    
    def fun():
        print("33[32m-------------开始打印-------------------33[0m")
        print("33[31min the process of %s33[0m" %os.getpid())
        time.sleep(1)
        print("进程%s执行完毕" %os.getpid())
    
    def bar(arg):
        '''当一个子线程执行完毕之后,父线程调用回调函数,比如公司发完工资之后,会发一条短信到你手机,这样就可以用回调函数'''
        print("您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!")
    
    if __name__ == "__main__":
        pool = Pool(processes=5)                                           #定义同一时间最大运行进程的个数
        for i in range(10):
            # pool.apply(func=fun)
            pool.apply_async(func=fun,callback=bar)
    
        print("The end!")
        pool.close()
        pool.join()
    执行如下:
    -------------开始打印-------------------
    in the process of 13778
    -------------开始打印-------------------
    in the process of 13779
    -------------开始打印-------------------
    in the process of 13780
    进程13776执行完毕
    进程13777执行完毕
    进程13778执行完毕
    进程13780执行完毕
    进程13779执行完毕
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    -------------开始打印-------------------
    in the process of 13776
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    -------------开始打印-------------------
    in the process of 13779
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    -------------开始打印-------------------
    in the process of 13778
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    -------------开始打印-------------------
    in the process of 13777
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    -------------开始打印-------------------
    in the process of 13780
    进程13777执行完毕
    进程13779执行完毕
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    进程13776执行完毕
    进程13778执行完毕
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    进程13780执行完毕
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!

        上面程序中,我们给apply_async()里面的参数callback=bar,进行了赋值,让执行完前面的线程之后,由主线程调用回调函数,记住是主线程调用的回调函数,而不是子线程调用的。下面程序我们打印是谁调用的,就能验证这一说法:

    from multiprocessing import Pool,Lock
    import time,os
    '''进程池,控制同一时间执行进程的个数,防止CPU运行吃力,启用进程很耗费CPU'''
    
    def fun():
        print("33[32m-------------开始打印-------------------33[0m")
        print("33[31min the process of %s33[0m" %os.getpid())
        time.sleep(1)
        print("进程%s执行完毕" %os.getpid())
        print("子线程的PID:",os.getpid())
    
    def bar(arg):
        '''当一个子线程执行完毕之后,父线程调用回调函数,比如公司发完工资之后,会发一条短信到你手机,这样就可以用回调函数'''
        print("您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!")
        print("bar的线程PID:",os.getpid())
    
    if __name__ == "__main__":
        pool = Pool(processes=5)                                           #定义同一时间最大运行进程的个数
        print("主线程的PID:",os.getpid())
        for i in range(10):
            # pool.apply(func=fun)
            pool.apply_async(func=fun,callback=bar)
    
        print("The end!")
        pool.close()
        pool.join()

        程序的执行结果如下:

    主线程的PID: 13866
    The end!
    -------------开始打印-------------------
    in the process of 13867
    -------------开始打印-------------------
    in the process of 13869
    -------------开始打印-------------------
    in the process of 13868
    -------------开始打印-------------------
    in the process of 13870
    -------------开始打印-------------------
    in the process of 13871
    进程13868执行完毕
    进程13867执行完毕
    子线程的PID: 13867
    子线程的PID: 13868
    进程13870执行完毕
    进程13871执行完毕
    子线程的PID: 13870
    子线程的PID: 13871
    进程13869执行完毕
    子线程的PID: 13869
    -------------开始打印-------------------
    in the process of 13868
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    bar的线程PID: 13866
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    bar的线程PID: 13866
    -------------开始打印-------------------
    in the process of 13869
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    bar的线程PID: 13866
    -------------开始打印-------------------
    in the process of 13870
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    -------------开始打印-------------------
    in the process of 13867
    bar的线程PID: 13866
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    bar的线程PID: 13866
    -------------开始打印-------------------
    in the process of 13871
    进程13868执行完毕
    子线程的PID: 13868
    进程13871执行完毕
    子线程的PID: 13871
    进程13869执行完毕
    子线程的PID: 13869
    进程13867执行完毕
    子线程的PID: 13867
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    bar的线程PID: 13866
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    bar的线程PID: 13866
    进程13870执行完毕
    子线程的PID: 13870
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    bar的线程PID: 13866
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    bar的线程PID: 13866
    您好,您的工资已到账,请注意查收,如有疑问,请联系人事部,谢谢!!!
    bar的线程PID: 13866

        从上面的bar函数的线程PID可以看出,bar的PID与主程序的PID是一样的,说明是主程序调用的回调函数,如果pool.close()关闭之后不join(),则程序会关闭,不管其他进程是否执行完毕。

        操作完成数据库后,写一个日志,为什么不用子进程,子进程会连接N次,使用callback只需要主程序连接一次即可,提高效率。

        join()    主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用

        有两个值得提到的,一个是 callback,另外一个是 multiprocessing.pool.AsyncResult。 callback 是在结果返回之前,调用的一个函数,这个函数必须只有一个参数,它会首先接收到结果。callback 不能有耗时操作,因为它会阻塞主线程。     

  • 相关阅读:
    QPS、TPS、RT、并发数、吞吐量理解和性能优化深入思考
    从开源协议到谷歌禁用华为、Docker实体清单事件
    如何画好架构图?
    使用委托的异步方法
    里氏替换原则(转)
    HTTP协议详解(转)
    httpApplication事件和asp.net生命周期(整理)
    WebDev.WebServer.exe
    正则表达式的3种匹配模式
    Code First实体与数据表之间的映射关系
  • 原文地址:https://www.cnblogs.com/gengcx/p/7502182.html
Copyright © 2020-2023  润新知