python 进程 线程

------------恢复内容开始------------

 

一、进程和线程区别

  1.简介

    进程是系统进行资源分配和调度的一个独立单位.     资源的调度    

    线程是进程的一个实体,是CPU调度和分派的基本单位

 

    简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

    从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。

  进程 (process  车间) 线程 (thread 工人)
简介 多种资源调度的基本单位 cpu调度的基本单位
资源 独立的内存单元 多个线程共享同进程的内存
启动速度
关系 一个程序至少有一个进程   进程之间独立 一个进程至少有一个线程.   
子进程相当于克隆了父进程 相互独立  主线程,子线程,守护线程
数据 进程之间不能相互交流 线程之间可以相互交流
     

 

 

    经典解释:

    https://www.cnblogs.com/lmule/archive/2010/08/18/1802774.html

    形象解释:

    https://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

 

二、pythonGIL (Global Interpreter Lock)   python缺陷点

  •  上面的核心意思就是,无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行
  •  “假多线程”  基于cpython解析器的原因,其实只有一个线程在工作,pypy解释器是趋势
  • 首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL

    这篇文章透彻的剖析了GIL对python多线程的影响,强烈推荐看一下:http://www.dabeaz.com/python/UnderstandingGIL.pdf 

 

三、线程

  python多线程 不适合cpu密集操作型的任务,适合io操作密集型的任务。

  1.线程实践 python threading模块

    两种调用方式:

   (1)直接调用  (常用)

    •  
      import threading
      import time
       
      def sayhi(num): #定义每个线程要运行的函数
       
          print("running on number:%s" %num)
       
          time.sleep(3)
       
      if __name__ == '__main__':
       
          t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
          t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例
       
          t1.start() #启动线程
          t2.start() #启动另一个线程
       
          print(t1.getName()) #获取线程名
          print(t2.getName())

       

 

    (2)继承式调用

    • import threading
      import time
       
       
      class MyThread(threading.Thread):
          def __init__(self,num):
              threading.Thread.__init__(self)
              self.num = num
       
          def run(self):#定义每个线程要运行的函数
       
              print("running on number:%s" %self.num)
       
              time.sleep(3)
       
      if __name__ == '__main__':
       
          t1 = MyThread(1)
          t2 = MyThread(2)
          t1.start()
          t2.start()

       

          

      (3) 实例 50个线程

    •   
      import threading
      import time
      
      def run(n):
          print("test", n)
          time.sleep(2)
          print("done", n)
      
      start_time = time.time()
      t_objs = []
      
      for i in range(50):    # 50 个线程
          t = threading.Thread(target=run, args=("t-%s" % i,))
          t.start()
          t_objs.append(t)
      
      for t in t_objs:     # 不影响多线程变成串行
          t.join()    # 等所有的子线程执行完再走主线程
      
      print("---all threads has finished-----")   #
      print("cost:", time.time() - start_time)

       

 

   2.主线程 子线程

   threading.current_thread()  # 查看线程是主还是子

   threading.active_count())   # 查看线程个数

  • print("---all threads has finished-----", threading.current_thread(),threading.active_count())

     

  3.守护线程   setDaemon(True)    join 等待线程结束

     守护线程(salve) 服务于 非守护线程(master)

    如果你设置一个线程为守护线程,,就表示你在说这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。

  • import threading
    import time
    
    def run(n):
        print("test", n)
        time.sleep(2)
        print("done", n)
    
    start_time = time.time()
    t_objs = []   # 存线程实例
    
    for i in range(50):    # 50 个线程
        t = threading.Thread(target=run, args=("t-%s" % i,))
        t.setDaemon(True)  # 把当前线程设置为守护线程
        t.start()
        t_objs.append(t)  # 为了不阻塞后面线程的启动,不在这join,先放到一个列表中
    
    # for t in t_objs:     # 循环线程实例列表, 等待所有的线程执行完毕
    #     t.join()
    
    print("---all threads has finished-----")
    print("cost:", time.time() - start_time)

     

  4. 线程锁(互斥锁Mutex)

     一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况?

    • import time
      import threading
       
      def addNum():
          global num #在每个线程中都获取这个全局变量
          print('--get num:',num )
          time.sleep(1)
          num  -=1 #对此公共变量进行-1操作
       
      num = 100  #设定一个共享变量
      thread_list = []
      for i in range(100):
          t = threading.Thread(target=addNum)
          t.start()
          thread_list.append(t)
       
      for t in thread_list: #等待所有线程执行完毕
          t.join()
       
       
      print('final num:', num )

       

     正常来讲,这个num结果应该是0, 但在python 2.7上多运行几次,会发现,最后打印出来的num结果不总是0,为什么每次运行的结果不一样呢? 哈,很简单,假设你有A,B两个线程,此时都 要对num 进行减1操作, 由于2个线程是并发同时运行的,所以2个线程很有可能同时拿走了num=100这个初始变量交给cpu去运算,当A线程去处完的结果是99,但此时B线程运算完的结果也是99,两个线程同时CPU运算的结果再赋值给num变量后,结果就都是99。那怎么办呢? 很简单,每个线程在要修改公共数据时,为了避免自己在还没改完的时候别人也来修改此数据,可以给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。 

*注:不要在3.x上运行,不知为什么,3.x上的结果总是正确的,可能是自动加了锁

加锁版本

    • import time
      import threading
       
      def addNum():
          global num #在每个线程中都获取这个全局变量
          print('--get num:',num )
          time.sleep(1)
          lock.acquire() #修改数据前加锁
          num  -=1 #对此公共变量进行-1操作
          lock.release() #修改后释放
       
      num = 100  #设定一个共享变量
      thread_list = []
      lock = threading.Lock() #生成全局锁
      for i in range(100):
          t = threading.Thread(target=addNum)
          t.start()
          thread_list.append(t)
       
      for t in thread_list: #等待所有线程执行完毕
          t.join()
       
      print('final num:', num )

       

  5、GIL VS Lock 

    机智的同学可能会问到这个问题,就是既然你之前说过了,Python已经有一个GIL来保证同一时间只能有一个线程来执行了,为什么这里还需要lock? 注意啦,这里的lock是用户级的lock,跟那个GIL没关系 ,具体我们通过下图来看一下+配合我现场讲给大家,就明白了。

      那你又问了, 既然用户程序已经自己有锁了,那为什么C python还需要GIL呢?加入GIL主要的原因是为了降低程序的开发的复杂度,比如现在的你写python不需要关心内存回收的问题,因为Python解释器帮你自动定期进行内存回收,你可以理解为python解释器里有一个独立的线程,每过一段时间它起wake up做一次全局轮询看看哪些内存数据是可以被清空的,此时你自己的程序 里的线程和 py解释器自己的线程是并发运行的,假设你的线程删除了一个变量,py解释器的垃圾回收线程在清空这个变量的过程中的clearing时刻,可能一个其它线程正好又重新给这个还没来及得清空的内存空间赋值了,结果就有可能新赋值的数据被删除了,为了解决类似的问题,python解释器简单粗暴的加了锁,即当一个线程运行时,其它人都不能动,这样就解决了上述的问题,  这可以说是Python早期版本的遗留问题。

   

  6、RLock(递归锁)

    说白了就是在一个大锁中还要再包含子锁,类似字典 key value对应,使用场景不多

    • import threading,time
       
      def run1():
          print("grab the first part data")
          lock.acquire()
          global num
          num +=1
          lock.release()
          return num
      def run2():
          print("grab the second part data")
          lock.acquire()
          global  num2
          num2+=1
          lock.release()
          return num2
      def run3():
          lock.acquire()
          res = run1()
          print('--------between run1 and run2-----')
          res2 = run2()
          lock.release()
          print(res,res2)
       
       
      if __name__ == '__main__':
       
          num,num2 = 0,0
          lock = threading.RLock()
          for i in range(10):
              t = threading.Thread(target=run3)
              t.start()
       
      while threading.active_count() != 1:
          print(threading.active_count())
      else:
          print('----all threads done---')
          print(num,num2)

       

  7、Semaphore(信号量)   

    互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。

    semaphore是一个内置的计数器

    每当调用acquire()时,内置计数器-1
    每当调用release()时,内置计数器+1

    计数器不能小于0,当计数器为0时,acquire()将阻塞线程直到其他线程调用release()

    • import threading,time
       
      def run(n):
          semaphore.acquire()
          time.sleep(1)
          print("run the thread: %s\n" %n)
          semaphore.release()
       
      if __name__ == '__main__':
       
          num= 0
          semaphore  = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
          for i in range(20):
              t = threading.Thread(target=run,args=(i,))
              t.start()
       
      while threading.active_count() != 1:
          pass #print threading.active_count()
      else:
          print('----all threads done---')
          print(num)


      结果:
      同时执行5个

       

  8、timer

 

  9、Events(事件)

    简介:线程之间用于交互的一个对象,这个event是一个内部的标签,线程可以等待这个标签的状态

#客户端线程可以等待标志设置

event.wait()

#服务器线程可以设置或重置它
event.set()
event.clear()
如果设置了该标志,则wait方法不会执行任何操作。
如果清除了该标志,则等待将阻塞,直到重新设置为止。
任何数量的线程都可以等待同一事件。

Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。

Event()

  • set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。

  • clear(): 将标志设为False。

  • wait(timeout): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。

  • isSet(): 获取内置标志状态,返回True或False。

 

    通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。

    •   
      import threading,time
      import random
      def light():
          if not event.isSet():
              event.set() #wait就不阻塞 #绿灯状态
          count = 0
          while True:
              if count < 10:
                  print('\033[42;1m--green light on---\033[0m')
              elif count <13:
                  print('\033[43;1m--yellow light on---\033[0m')
              elif count <20:
                  if event.isSet():
                      event.clear()
                  print('\033[41;1m--red light on---\033[0m')
              else:
                  count = 0
                  event.set() #打开绿灯
              time.sleep(1)
              count +=1
      def car(n):
          while 1:
              time.sleep(random.randrange(10))    # 生成随机数
              if  event.isSet(): #绿灯
                  print("car [%s] is running.." % n)
              else:
                  print("car [%s] is waiting for the red light.." %n)
      if __name__ == '__main__':
          event = threading.Event()
          Light = threading.Thread(target=light)
          Light.start()
          for i in range(3):
              t = threading.Thread(target=car,args=(i,))
              t.start()

       

 

 

    这里还有一个event使用的例子,员工进公司门要刷卡, 我们这里设置一个线程是“门”, 再设置几个线程为“员工”,员工看到门没打开,就刷卡,刷完卡,门开了,员工就可以通过。

    •    
      #_*_coding:utf-8_*_
      __author__ = 'Alex Li'
      import threading
      import time
      import random
      
      def door():
          door_open_time_counter = 0
          while True:
              if door_swiping_event.is_set():
                  print("\033[32;1mdoor opening....\033[0m")
                  door_open_time_counter +=1
      
              else:
                  print("\033[31;1mdoor closed...., swipe to open.\033[0m")
                  door_open_time_counter = 0 #清空计时器
                  door_swiping_event.wait()
      
      
              if door_open_time_counter > 3:#门开了已经3s了,该关了
                  door_swiping_event.clear()
      
              time.sleep(0.5)
      
      
      def staff(n):
      
          print("staff [%s] is comming..." % n )
          while True:
              if door_swiping_event.is_set():
                  print("\033[34;1mdoor is opened, passing.....\033[0m")
                  break
              else:
                  print("staff [%s] sees door got closed, swipping the card....." % n)
                  print(door_swiping_event.set())
                  door_swiping_event.set()
                  print("after set ",door_swiping_event.set())
              time.sleep(0.5)
      door_swiping_event  = threading.Event() #设置事件
      
      
      door_thread = threading.Thread(target=door)
      door_thread.start()
      
      
      
      for i in range(5):
          p = threading.Thread(target=staff,args=(i,))
          time.sleep(random.randrange(3))
          p.start()

       

  10、队列queue

    (1)简介:     

      队列类似于一条管道,元素先进先出,进put(arg),取get( )。

      需要注意的是:队列都是在内存中操作,进程退出,队列清空,另外,队列也是一个阻塞的形态。

      Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递。   先进先出(FIFO)

    (2)优点

      1. 提高效率

      2. 完成了程序的解耦。(耦合:程序之间的关联,依赖关系)

 

    (3)队列的分类

队列方式 特点
queue.Queue 先进先出队列
queue.LifoQueue(last in first out) 后进先出队列
queue.PriorityQueue 优先级队列
queue.deque 双线队列

 

    (4)队列方法

      

方法 用法说明
put

放数据,Queue.put( )默认有block=True和timeout两个参数。当block=True时,写入是阻塞式的,阻塞时间由timeout确定。当队列q被(其他线程)写满后,这段代码就会阻塞,直至其他线程取走数据。Queue.put()方法加上 block=False 的参数,即可解决这个隐蔽的问题。但要注意,非阻塞方式写队列,当队列满时会抛出 exception Queue.Full 的异常

get

取数据(默认阻塞),Queue.get([block[, timeout]])获取队列,timeout等待时间

empty

如果队列为空,返回True,反之False

qsize

显示队列中真实存在的元素长度

maxsize

最大支持的队列长度,使用时无括号

join

实际上意味着等到队列为空,再执行别的操作

task_done

在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号

full

如果队列满了,返回True,反之False

 

    (5) 单向队列   queue.Queue

    • import queue
      
      q=queue.Queue(5)    #如果不设置长度,默认为无限长
      print(q.maxsize)    #注意没有括号
      q.put(123)
      q.put(456)
      q.put(789)
      q.put(100)
      q.put(111)
      q.put(233)
      print(q.get())
      print(q.get())

      打印时候是阻塞的,因为创建了5个元素长度的队列,但我put进去了6个,所以就阻塞了。如果少写一个能显示出正确的123。

    •     
      q=queue.Queue(5)    #如果不设置长度,默认为无限长
      print(q.maxsize)    #注意没有括号
      q.put(123)
      q.put(456)
      q.put(789)
      q.put(100)
      q.put(111)
      print(q.get())
      print(q.get())
      -----------------------------------
      5
      123
      456
      
      print(q.get())
      789
      print(q.get())
      100
      print(q.get())
      111

       

    (6)后进先出队列    queue.LifoQueue()

      场景:买水果等

    • import queue
      
      q = queue.LifoQueue()
      
      
      q.put(1)
      q.put(2)
      q.put(3)
      
      print(q.get())
      print(q.get())
      print(q.get())

       

    (7)优先级队列  

      需要注意的是,优先级队列put的是一个元组,(优先级,数据),优先级数越小,级别越高

    •   
      import queue
      
      q = queue.PriorityQueue()
      
      q.put((6, "vip4"))
      q.put((10, "vip1"))
      q.put((-1, "vip2"))
      q.put((3, "vip3"))
      
      
      
      print(q.get())
      print(q.get())
      print(q.get())
      print(q.get())
      
      -----------------------------
      (-1, 'vip2')
      (3, 'vip3')
      (6, 'vip4')
      (10, 'vip1')

       

    (8) 双线队列

    • import queue
      
      q = queue.deque()
      q.append(123)
      q.append(456)
      q.appendleft(780)
      print(q)
      print(q.pop())
      print(q.popleft())
      -----------------------------
      deque([780, 123, 456])
      456
      780

       

 

  11、生产者消费者模型   

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

场景: 集群

为什么要使用生产者和消费者模式

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

什么是生产者消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

 

下面来学习一个最基本的生产者消费者模型的例子

    • import threading
      import time
      import queue
      
      q = queue.Queue(10)
      
      
      def Producer(name):
          count = 1
          while True:
              q.put("骨头%s" % count)
              print("生产了骨头", count)
              count += 1
              time.sleep(0.5)
      
      
      def Consumer(name):
          while True:
              print("[%s] 取到 [%s] 并且吃了它。。。" %(name, q.get()))
              time.sleep(1)
      
      
      p = threading.Thread(target=Producer, args=("vip",))
      c = threading.Thread(target=Consumer, args=("dog",))
      c1 = threading.Thread(target=Consumer, args=("dog2",))
      
      
      p.start()
      c.start()
      c1.start()
    • import threading
      import queue
       
      def producer():
          for i in range(10):
              q.put("骨头 %s" % i )
       
          print("开始等待所有的骨头被取走...")
          q.join()
          print("所有的骨头被取完了...")
       
       
      def consumer(n):
       
          while q.qsize() >0:
       
              print("%s 取到" %n  , q.get())
              q.task_done() #告知这个任务执行完了
       
       
      q = queue.Queue()
       
       
       
      p = threading.Thread(target=producer,)
      p.start()
       
      c1 = consumer("李闯")
      View Code
    • import time,random
      import queue,threading
      q = queue.Queue()
      def Producer(name):
        count = 0
        while count <20:
          time.sleep(random.randrange(3))
          q.put(count)
          print('Producer %s has produced %s baozi..' %(name, count))
          count +=1
      def Consumer(name):
        count = 0
        while count <20:
          time.sleep(random.randrange(4))
          if not q.empty():
              data = q.get()
              print(data)
              print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
          else:
              print("-----no baozi anymore----")
          count +=1
      p1 = threading.Thread(target=Producer, args=('A',))
      c1 = threading.Thread(target=Consumer, args=('B',))
      p1.start()
      c1.start()
      View Code

       

 

四、进程

  1.多进程    

  Python多进程方面涉及的模块主要包括:

 

    • subprocess:可以在当前程序中执行其他程序或命令;
    • mmap:提供一种基于内存的进程间通信机制;
    • multiprocessing:提供支持多处理器技术的多进程编程接口,并且接口的设计最大程度地保持了和threading模块的一致,便于理解和使用。

subprocess介绍   

https://www.cnblogs.com/Security-Darren/p/4733368.html

 

mmap介绍

https://www.cnblogs.com/Security-Darren/p/4733387.html

 

multiprocessing介绍

Python中的多进程是通过multiprocessing包来实现的,和多线程的threading.Thread差不多,它可以利用multiprocessing.Process对象来创建一个进程对象。这个进程对象的方法和线程对象的方法差不多也有start(), run(), join()等方法,其中有一个方法不同Thread线程对象中的守护线程方法是setDeamon,而Process进程对象的守护进程是通过设置daemon属性来完成的。

下面说说Python多进程的实现方法,和多线程类似

    • 简单的多进程
      import multiprocessing
      import time
      
      
      def run(name):
          time.sleep(2)
          print("hello", name)
      
      
      if __name__ == '__main__':
          for i in range(10):
              p = multiprocessing.Process(target=run, args=("bob %s" % i,))
              p.start()

       

    • 多进程里面有线程
      import multiprocessing
      import time,threading
      
      
      def thread_run():
          print(threading.get_ident())  # 获取线程号
      
      
      def run(name):
          print("hello", name)
          t = threading.Thread(target=thread_run,)
          t.start()
          time.sleep(2)
      
      
      if __name__ == '__main__':
          for i in range(10):
              p = multiprocessing.Process(target=run, args=("bob %s" % i,))
              p.start()

      -----------------------------------

      hello bob 0
      123145327697920
      hello bob 1
      123145327697920
      hello bob 2
      123145327697920
      hello bob 3
      123145327697920
      hello bob 4
      123145327697920
      hello bob 5
      123145327697920
      hello bob 6
      123145327697920
      hello bob 7
      123145327697920
      hello bob 8
      123145327697920
      hello bob 9
      123145327697920

       

       

2.进程间的通信

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

  (1)Queues  队列

      使用方法跟threading里的queue差不多

      Queue在多线程中也说到过,在生成者消费者模式中使用,是线程安全的,是生产者和消费者中间的数据管道,那在python多进程中,它其实就是进程之间的数据管道,实现进程通信。

    •  
      from multiprocessing import Process, Queue
      
      
      def f(q):
          q.put([42, None, 'hello'])
      
      
      if __name__ == '__main__':
          q = Queue()
          p = Process(target=f, args=(q,))
          p.start()
          print(q.get())   
          p.join()
      
      ------------------------
      [42, None, 'hello']

       

    • 例子2
      from multiprocessing import Process,Queue
      
      
      def fun1(q,i):
          print('子进程%s 开始put数据' %i)
          q.put('我是%s 通过Queue通信' %i)
      
      if __name__ == '__main__':
          q = Queue()
      
          process_list = []
          for i in range(3):
              p = Process(target=fun1,args=(q,i,))  #注意args里面要把q对象传给我们要执行的方法,这样子进程才能和主进程用Queue来通信
              p.start()
              process_list.append(p)
      
          for i in process_list:
              p.join()
      
          print('主进程获取Queue数据')
          print(q.get())
          print(q.get())
          print(q.get())
          print('结束测试')
      
      --------------------------------------------------
      子进程0 开始put数据
      子进程1 开始put数据
      子进程2 开始put数据
      主进程获取Queue数据
      我是0 通过Queue通信
      我是1 通过Queue通信
      我是2 通过Queue通信
      结束测试
      View Code

       

  (2)Pipes 管道

    管道Pipe和Queue的作用大致差不多,也是实现进程间的通信,下面之间看怎么使用吧

    • from multiprocessing import Process, Pipe
      def fun1(conn):
          print('子进程发送消息:')
          conn.send('你好主进程')
          print('子进程接受消息:')
          print(conn.recv())
          conn.close()
      
      if __name__ == '__main__':
          conn1, conn2 = Pipe() #关键点,pipe实例化生成一个双向管
          p = Process(target=fun1, args=(conn2,)) #conn2传给子进程
          p.start()
          print('主进程接受消息:')
          print(conn1.recv())
          print('主进程发送消息:')
          conn1.send("你好子进程")
          p.join()
          print('结束测试')
      
      ----------------------------------
      主进程接受消息:
      子进程发送消息:
      子进程接受消息:
      你好主进程
      主进程发送消息:
      你好子进程
      结束测试

       

  (3) Managers

   Queue和Pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。那么就要用到Managers

    • from multiprocessing import Process, Manager
      import os
      def f(d, l):
          d[os.getpid()] =os.getpid()
          l.append(os.getpid())
          print(l)
      
      if __name__ == '__main__':
          with Manager() as manager:
              d = manager.dict() #{} #生成一个字典,可在多个进程间共享和传递
      
              l = manager.list(range(5))#生成一个列表,可在多个进程间共享和传递
              p_list = []
              for i in range(10):
                  p = Process(target=f, args=(d, l))
                  p.start()
                  p_list.append(p)
              for res in p_list: #等待结果
                  res.join()
      
              print(d)
              print(l)
      
      ----------------------------------
      [0, 1, 2, 3, 4, 9830]
      [0, 1, 2, 3, 4, 9830, 9831]
      [0, 1, 2, 3, 4, 9830, 9831, 9832]
      [0, 1, 2, 3, 4, 9830, 9831, 9832, 9833]
      [0, 1, 2, 3, 4, 9830, 9831, 9832, 9833, 9834]
      [0, 1, 2, 3, 4, 9830, 9831, 9832, 9833, 9834, 9835]
      [0, 1, 2, 3, 4, 9830, 9831, 9832, 9833, 9834, 9835, 9836]
      [0, 1, 2, 3, 4, 9830, 9831, 9832, 9833, 9834, 9835, 9836, 9837]
      [0, 1, 2, 3, 4, 9830, 9831, 9832, 9833, 9834, 9835, 9836, 9837, 9838]
      [0, 1, 2, 3, 4, 9830, 9831, 9832, 9833, 9834, 9835, 9836, 9837, 9838, 9839]
      {9830: 9830, 9831: 9831, 9832: 9832, 9833: 9833, 9834: 9834, 9835: 9835, 9837: 9837, 9836: 9836, 9838: 9838, 9839: 9839}
      [0, 1, 2, 3, 4, 9830, 9831, 9832, 9833, 9834, 9835, 9836, 9837, 9838, 9839]

       

  (4) 进程同步   lock   

    如果不使用来自不同进程的锁输出,则很容易混淆所有信息

    • from multiprocessing import Process, Lock
       
      def f(l, i):
          l.acquire()
          try:
              print('hello world', i)
          finally:
              l.release()
       
      if __name__ == '__main__':
          lock = Lock()
       
          for num in range(10):
              Process(target=f, args=(lock, num)).start()
      View Code

       

 

 

3.进程池

进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。就是固定有几个进程可以使用。

为什么:防止进程占用太多资源。

进程池中有两个方法:

apply:同步,一般不使用

apply_async:异步

    • from  multiprocessing import Process,Pool
      import os, time, random
      
      def fun1(name):
          print('Run task %s (%s)...' % (name, os.getpid()))
          start = time.time()
          time.sleep(random.random() * 3)   # 随机生成的一个实数
          end = time.time()
          print('Task %s runs %0.2f seconds.' % (name, (end - start)))
      
      if __name__=='__main__':
          pool = Pool(5) #创建一个5个进程的进程池
      
          for i in range(10):
              pool.apply_async(func=fun1, args=(i,))
      
          pool.close()
          pool.join()
          print('结束测试')
      
      ---------------------------
      
      Run task 0 (9889)...
      Run task 1 (9890)...
      Run task 2 (9891)...
      Run task 3 (9892)...
      Run task 4 (9893)...
      Task 2 runs 0.16 seconds.
      Run task 5 (9891)...
      Task 4 runs 0.40 seconds.
      Run task 6 (9893)...
      Task 1 runs 0.88 seconds.
      Run task 7 (9890)...
      Task 6 runs 0.49 seconds.
      Run task 8 (9893)...
      Task 5 runs 1.33 seconds.
      Run task 9 (9891)...
      Task 3 runs 1.87 seconds.
      Task 8 runs 1.26 seconds.
      Task 0 runs 2.35 seconds.
      Task 9 runs 1.30 seconds.
      Task 7 runs 2.70 seconds.
      结束测试

       

 

 

 

 

 

五、

 

 

 

------------恢复内容结束------------

subprocess

posted @ 2020-05-17 18:58  扶我起来写代码  阅读(116)  评论(0)    收藏  举报