运维开发网

python3高级系列03:进程和线程(上)

运维开发网 https://www.qedev.com 2021-02-27 08:30 出处:51CTO 作者:mb5fdb0a4002420
进程、线程的概念如今使用电脑,我们可以同时聊天,听音乐,网上购物等,操作系统可以同时执行多个任务,每一个任务就是一个进程(process)。windows系统,我们可以在任务管理器里看到当前系统正在运行的任务。进程是程序运行的实例。线程(thread)是操作系统进行运算调度的最小单位,通常作为一个进程的处理单位,即一个进程可以包含多个线程,由线程完成具体的工作。如播放音乐时,一个线程播放音频,另一

python3高级系列03:进程和线程(上)

进程、线程的概念

如今使用电脑,我们可以同时聊天,听音乐,网上购物等,操作系统可以同时执行多个任务,每一个任务就是一个进程(process)。windows系统,我们可以在任务管理器里看到当前系统正在运行的任务。进程是程序运行的实例。

线程(thread)是操作系统进行运算调度的最小单位,通常作为一个进程的处理单位,即一个进程可以包含多个线程,由线程完成具体的工作。如播放音乐时,一个线程播放音频,另一个线程滚动显示歌词。

python3高级系列03:进程和线程(上)

python3高级系列03:进程和线程(上)

创建进程

下面我们使用multiprocessingmo模块的Process来创建一个进程。

from multiprocessing import Process

import os

def p(*name):

  print('p process',name,os.getpid(),os.getppid())

def m():

  print('m process start...',os.getpid())

  p1 = Process(target=p, name='main', args=('小麦','菜'))

  p1.start()

  p1.join()

  print('m process end...',os.getpid())

if __name__ == '__main__':

  m()

上面我们通过一个简单的示例,尽可能多的展示了Process的属性和方法,创建Process和我们之前创建类实例一样,可以指定进程name,进程所要调用的对象target及参数args。

可以通过os模块getpid方法获取当前进程pid,使用getppid方法获取当前进程的父进程parent pid。最后为了让子进程在父进程之前结束运行,我们调用了子进程的join方法。

上面的示例已经可以应对一些简单的小任务了,缺点也很明显,创建的进程和被调用的对象分离,且非面向对象。

进程启动start起来,执行的就是Process的run方法,所以用面向对象的思维很容易想到继承Process类,并重写run方法。下面我们通过进程子类来创建进程。

from multiprocessing import Process

import os

import time

class MyProcess(Process):

  def __init__(self,sleeps=1,name=''):

    Process.__init__(self)

    self.sleeps = sleeps

    if name:

      self.name = name

  def run(self):

    print('start...进程:{},pid:{},ppid:{}'.format(self.name,os.getpid(),os.getppid()))

    start = time.time()

    time.sleep(self.sleeps)

    waste = time.time()-start

    print('end...进程:{},pid:{},ppid:{},耗时:{:f}'.format(self.name,os.getpid(),os.getppid(),waste))

if __name__ == '__main__':

  print('start...main进程,pid:{:d},ppid:{:d}'.format(os.getpid(),os.getppid()))

  m1 = MyProcess(2,'m1')

  m2 = MyProcess(1,'m2')

  m1.start()

  m2.start()

  m1.join()

  m2.join()

  print('end...main进程,pid:{:d},ppid:{:d}'.format(os.getpid(), os.getppid()))

上面的示例我们创建了自己的进程类,进程启动调用自己的start方法,执行结果可以看到,m1和m2均有main进程创建。注意join方法是等待调用进程执行完毕,这里是使main进程等待m1和m2执行完成,m1和m2之间并无等待关系。

python3高级系列03:进程和线程(上)

上面我们分别使用Process和继承Process的方式创建进程,每次都需要单独创建,如果一次要创建多个进程需要实例化许多对象,且上述方法一个进程只能执行一个任务。针对以上问题,我们可以使用进程池Pool来创建进程。

def job(job_name):

  sleeps = random.randint(5,10)

  print('start sub pid:{:5d},job:{:s},sleeps:{:d}'.format(os.getpid(),job_name,sleeps))

  time.sleep(sleeps)

  print('end sub pid:{:5d},job:{:s},sleeps:{:d}'.format(os.getpid(),job_name,sleeps))

if __name__ == '__main__':

  p = Pool(3)

  for i in range(3):

    p.apply_async(job,('reading'+str(i),))

  p.close()

  p.join()

使用进程池可以非阻塞执行任务,注意这里的进程池p先关闭不再接受其他任务,才能使用join等待进程池p执行完成。

python3高级系列03:进程和线程(上)

python3高级系列03:进程和线程(上)

进程间通信

两个进程间如何通信,即一个进程发送数据给另一个进程,笔者首先想到的是全局变量,两个进程通过修改全局变量的值来进行数据通信。下面我们使用全局变量验证下,能否成功通信。

from multiprocessing import Process

total = 100

def incr():

  global total

  total += 1

  print('incr...',total)

  return total

def decr():

  global total

  total -= 1

  print('decr...', total)

  return total

if __name__ == '__main__':

  s = Process(target=incr,name='s')

  t = Process(target=decr,name='t')

  s.start()

  t.start()

  s.join()

  t.join()

并没有出现什么冲突的情况,两个进程都正确执行了代码逻辑,结果就是两个进程并没有共享到全局变量,可见全局变量无法在进程间共享数据。

python3高级系列03:进程和线程(上)

在python中可以使用multiprocessing模块的Queue队列来通信,我们先简单介绍下队列。

from multiprocessing import Queue

q = Queue(3)

for i in range(3):

  if not q.full():

    q.put(i)

print(q.qsize(),q.full())

if not q.empty():

  for i in range(3):

    print('**',q.get_nowait())

print(q.qsize(),q.empty())

队列是一种先进先出的数据结构,可以使用put方法往队列里放数据,使用get从队列取数据,注意这里由于是multiprocess模块队列,full,empty可能由于多进程处理,不是非常可靠。具体可以参考:

官方文档multiprocessing.Queue

https://docs.python.org/3.8/library/multiprocessing.html?highlight=queue#multiprocessing.Queue

python3高级系列03:进程和线程(上)

下面我们将put和get操作放到两个进程中。

from multiprocessing import Process,Queue

def fill(q):

  if not q.full():

    for item in ['pycharm','pygame','tkinter']:

      q.put(item)

def take(q):

  while not q.empty():

    print(q.get(True,2))

if __name__ == '__main__':

  q = Queue(3)

  p1,p2 = Process(target=fill,args=(q,)),Process(target=take,args=(q,))

  p1.start()

  p2.start()

  p1.join()

  p2.join()

运行结果能够看到可以两个进程可以通信了,其实只是把共享变量传到了两个进程中,完成共享队列,从而完成数据通信。注意这里依然可能出现p2取不到数据的情况,当我们先启动p2时,就更容易看到p2没有获取到数据。

扫码领视频副本.gif

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号