python多进程假死

结论:python多进程间用Queue通信时,如果子进程操作Queue满了或者内容比较大的情况下,该子进程会阻塞等待取走Queue内容(如果Queue数据量比较少,不会等待),如果调用join,主进程将处于等待,等待子进程结束,造成死锁

解决方式:在调用join前,及时把Queue的数据取出,而且Queue.get需要在join前

原理分析

模拟子进程阻塞:

  1.  
    from multiprocessing import Process, Queue
  2.  
     
  3.  
     
  4.  
    def fun(q):
  5.  
    num = 1000000000
  6.  
    q.put('=' * num)
  7.  
    print("done put")
  8.  
     
  9.  
     
  10.  
    if __name__ == '__main__':
  11.  
    queue = Queue()
  12.  
    p = Process(target=fun, args=(queue,))
  13.  
    p.start()
  14.  
    p.join()
  15.  
    print("done")

原因分析:

  1.  
    #
  2.  
    # Queue type using a pipe, buffer and thread
  3.  
    #
  4.  
     
  5.  
    class Queue(object):

multiprocessing.Queue底层是基于Pipe构建,操作系统管道不是无限长,因此子进程在执行put()期间,处于阻塞,直到某些其他进程使用get()从队列中取走数据。上例中,主进程等待子进程,打印不了done

而当队列put数据比较少时,是没有问题的,先打印done put,再打印done,但这样写法是有隐患,当put数据比较多时,就会阻塞

  1.  
    from multiprocessing import Process, Queue
  2.  
     
  3.  
     
  4.  
    def fun(q):
  5.  
    num = 1
  6.  
    q.put('=' * num)
  7.  
    print("done put")
  8.  
     
  9.  
     
  10.  
    if __name__ == '__main__':
  11.  
    queue = Queue()
  12.  
    p = Process(target=fun, args=(queue,))
  13.  
    p.start()
  14.  
    p.join()
  15.  
    print("done")

正确的写法

  1.  
    from multiprocessing import Process, Queue
  2.  
     
  3.  
     
  4.  
    def fun(q):
  5.  
    num = 1000000000
  6.  
    q.put('=' * num)
  7.  
    print("done put")
  8.  
     
  9.  
     
  10.  
    if __name__ == '__main__':
  11.  
    queue = Queue()
  12.  
    p = Process(target=fun, args=(queue,))
  13.  
    p.start()
  14.  
    queue.get()
  15.  
    p.join()
  16.  
    print("done")

在join前面调用queue.get

注意!!!以下这样写法也是不对的,join要在queue.get前面,不然主进程等待子进程结束,而子进程等待队列数据取走,造成死锁

  1.  
    p = Process(target=fun, args=(queue,))
  2.  
    p.start()
  3.  
    p.join()
  4.  
    queue.get()

Python多线程补充

Python 是一门解释型语言,它的执行是由解释器来控制的。

GIL,全称是 Global Interpreter Lock ,全局解释锁 ,专门给解释器用

一般情况下在用户态下是无法做到线程级别的时间片轮转

但是 python 能做到!python 里,解释器可以记录每一个线程执行了多长时间——时间一到,就能够切换到另一条线程。

GIL 就是拿来给线程加锁的,当一个线程将要执行时,解释器会把 GIL 锁给这个线程,其他线程因为没有锁,是无法运行的。等到持有锁线程阻塞或者运行 100 个字节码,解释器就会把锁交给其他线程。

但是这个 GIL 锁是全局(Global)的,也就导致即使是多核情况下,一次也只有一个线程能运行,从整体上看,整个程序是串行的。

python多线程应用

拿爬虫程序来说吧,单个爬虫总会花时间在下载网页上,很多 CPU 时间就浪费掉了,提供 sleep 机制后,这些爬虫可以在等待下载时释放 GIL 锁,把机会让给其他爬虫,这样整体运行速度能够得到大幅提升

也就是说 Python 的多线程适合 I/O 密集型的程序,但是对计算密集型程序就不那么友好了

对于计算密集型程序用多进程或者让 python 调用 C 语言的代码,在 C 语言里实现多线程

原文地址:https://www.cnblogs.com/ExMan/p/13631694.html