No.33Process类

No.33

今日概要

  • 守护进程
  • Process类
  • 面向对象方式开启进程
  • 进程之间的通信

内容回顾

线程

  • 线程是进程的一部分,每个进程中至少有一个线程。
  • 能被CPU调度的最小单位
  • 数据共享
    • 一个进程中的多个线程可以共享这个进程的数据
  • 开销小
    • 线程的创建、销毁、切换所需开销远远小于进程

multiprocessing模块

  • Process类
    • 如何创建一个进程对象 → Process( target=函数名,args=(参数,) )
      • 对象和进程之间的关系
        • 进程对象和进程并没有直接关系
        • 进程对象只是存储了和进程相关的内容
        • 此时此刻操作系统还没有接到开启进程的指令
    • 如何开启一个进程
      • 对过对象.start()就开启了一个进程 → 相当于给了操作系统一条指令
      • start方法的非阻塞和异步特点
        • 我们既不等待这个进程开启,也不等待操作系统给我们的响应
        • 我们只是负责通知操作系统去开启一个进程
        • 当操作系统开启了一个子进程之后,主进程和子进程的代码完全异步
    • 父进程和子进程之间的关系
      • 父进程会等待子进程结束之后才结束
      • 目的是为了回收子进程的资源
    • 不同操作系统中进程开启的方式
      • windows
        • 通过再一次执行(模块导入)父进程文件中的代码来获取父进程中的数据。
        • 不希望被子进程执行的代码就写在if __name__ == '__main__'下。
      • ios/linux
        • 直接写即可,不用放在if __name__ == '__main__'下。
    • 如何确认一个子进程执行完毕
      • join方法 → 对象.join
        • 阻塞主进程,直到子进程执行完毕。
      • 开启多个子进程,等待所有子进程结束
        • 创建列表储存所有的进程对象
        • 循环列表中的进程对象来使用join方法

内容详细

1.守护进程

有一个参数可以将一个子进程设置为一个守护进程

# 守护进程随着主进程的代码结束而结束
import time
from multiprocessing import Process

def son1():
    while True:
        print('is alive')
        time.sleep(0.5)

if __name__ == '__main__':
    p = Process(target=son1)
    p.daemon = True  # 把子进程设置成了守护进程
    p.start()
    time.sleep(2)
import time
from multiprocessing import Process

def son1():
    while True:
        print('is alive')
        time.sleep(0.5)

def son2():
    for i in range(5):
        print('in son2')
        time.sleep(1)

if __name__ == '__main__':
    p1 = Process(target=son1)
    p1.daemon = True
    p.start()
    p2 = Process(target=son2)
    p2.start()
    time.sleep(2)

2.Process类

import time
from multiprocessing import Process

def son1():
    while True:
        print('is alive')
        time.sleep(0.5)

if __name__ == '__main__':
    p = Process(target=son1)
    p.start()  # 异步非阻塞
    print(p.is_alive())  # 判断子进程是否处于运行中
    time.sleep(1)
    p.terminate()  # 异步非阻塞:强制结束一个子进程
    print(p.is_alive())  # True,因为操作系统还没来得及关闭
    time.sleep(0.01)
    print(p.is_alive())  # False,操作系统已经响应了关闭进程的需求

什么是异步非阻塞?

  • terminate 是一个非常典型的异步非阻塞应用场景

3.面向对象方式开启进程

import os
from multiprocessing import Process

class MyProcess(Process):  # 必须继承Process类
    def run(self):  # 必须有run方法
        print(os.getpid(), os.getppid())

if __name__ == '__main__':
    mp = MyProcess()
    mp.start()
    print('main:', os.getpid())
import time
from multiprocessing import Process

class MyProcess1(Process):
    def run(self):
        for i in range(5):
            print('子进程要执行的代码封装进run方法中')

class MyProcess2(Process):
    def run(self):
        while True:
            print('自定义类中必须有run方法')
            time.sleep(0.2)

if __name__ == '__main__':
    mp1 = MyProcess1()
    mp1.start()
    mp2 = MyProcess2()
    mp2.daemon = True
    mp2.start()
    print('自定义类必须继承Process类')
    time.sleep(1)
import time
from multiprocessing import Process

class MyProcess(Process):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        super().__init__()

    def run(self):
        print(self.x, self.y)
        for i in range(5):
            print('666')
            time.sleep(0.2)

if __name__ == '__main__':
    mp = MyProcess(1, 2)
    mp.start()
    print('参数传递')
import os
from multiprocessing import Process

def func(arg):
    print(arg)

class MyProcess(Process):
    def __init__(self, target, args=()):
        super().__init__(target=target, args=args)

if __name__ == '__main__':
    mp = MyProcess(func, ('必须是元组',))
    mp.start()
    print('面向对象的方式开启子进程')

Process类

  • 开启进程的方式

    • 面向函数

      def 函数名():
      	'要在子进程中执行的代码'
          
      p = Process(target=函数名, args=(参数,))
      p.start() 
      
    • 面向对象

      class 类名(Process):
          def __init__(self, 参数1, 参数2): '如果子进程不需要参数则不写'
              self.a = 参数1
              self.b = 参数2
              super().__init__()
       
      	def run(self):
              '要在子进程中执行的代码'
      
      # 创建进程对象        
      p = 类名(参数1, 参数2)
      
      # Process类提供的操作进程的方法
      p.start()      '开启子进程,异步非阻塞'
      p.terminate()  '结束子进程,异步非阻塞'
      p.join()       '等子进程结束,同步阻塞'
      p.isalive()    '获取当前子进程的状态'
      daemon = True  '设置为守护进程,永远在主进程的代码执行结束之后自动结束'
      

4.锁

在一个并发的场景下,涉及修改共享数据资源时,则需要加锁来维护数据的安全。

import time
import json
from multiprocessing import Process, Lock # 导入Lock类

def search_ticket(user):
    with open('test', 'r', encoding='utf-8') as f:
        dic = json.load(f)
        print('%s查询结果: %s张余票' % (user, dic['count']))

def buy_ticket(user, lock):
'with lock:'  # with语句加锁
    lock.acquire()  # 给这段代码加锁
    time.sleep(0.02)
    with open('test') as f:
        dic = json.load(f)
    if dic['count'] > 0:
        print('%s买到票' % (user,))
        dic['count'] -= 1
    else:
        print('%s没买到票' % (user,))
    time.sleep(0.02)
    with open('test', 'w') as f:
        json.dump(dic, f)
    lock.release()  # 给这段代码解锁

def task(user, lock):
    search_ticket(user)
    buy_ticket(user, lock)

if __name__ == '__main__':
    lock = Lock()
    lst = []
    for i in range(1, 11):
        p = Process(target=task, args=('user%s' % i, lock))
        p.start()
        lst.append(p)

    for i in lst:
        i.join()
    with open('test', 'w') as f:
        dic = {"count": 1}
        json.dump(dic, f)
import time
import json
from multiprocessing import Process, Lock

def search_ticket(user):
    with open('test', 'r', encoding='utf-8') as f:
        dic = json.load(f)
        print('%s查询结果: %s张余票' % (user, dic['count']))

def buy_ticket(user):
    time.sleep(0.02)
    with open('test') as f:
        dic = json.load(f)
    if dic['count'] > 0:
        print('%s买到票' % (user,))
        dic['count'] -= 1
    else:
        print('%s没买到票' % (user,))
    time.sleep(0.02)
    with open('test', 'w') as f:
        json.dump(dic, f)

def task(user, lock):
    search_ticket(user)
    with lock:  # 推荐with语句加锁,自带异常处理功能。
        buy_ticket(user)

if __name__ == '__main__':
    lock = Lock()
    lst = []
    for i in range(1, 11):
        p = Process(target=task, args=('user%s' % i, lock))
        p.start()
        lst.append(p)

    for i in lst:
        i.join()
    with open('test', 'w') as f:
        dic = {"count": 1}
        json.dump(dic, f)

同步存在的意义

  • 数据的安全性
  • 在数据安全的基础上,再考虑效率问题。

总结

  • 步骤
    • 先在主进程中实例化 lock = Lock()
    • 再把实例化对象传给子进程
    • 在子进程中对需要加锁的代码进行with lock
      • with lock相当于lock.acquire()lock.release()
  • 应用场景
    • 多进程共享数据资源(文件,数据库)
    • 需要对共享资源进行修改、删除操作
  • 加锁之后能过够保证数据的安全性,但是也降低了程序的执行效率。

5.进程之间的通信

  • IPC(inter process communication)进程之间的通信
    • 通过文件实现
      • 使用别人封装好的功能
        • Queue 对列
          • multiprocessing模块中导入Queue实现
          • Queue基于socket模块pickle模块Lock模块实现
        • Pipe 管道
          • 没有锁,不安全
          • Pipe基于socket模块pickle模块实现
# 进程之间的数据隔离
from multiprocessing import Process

n = 100
def func():
    global n
    n -= 1
    print(n)

if __name__ == '__main__':
    lst = []
    for i in range(3):
        p = Process(target=func)
        p.start()
        lst.append(p)
    for p in lst:
        p.join()

    print('main',n)
    
结果:
99
99
99
main 100
from multiprocessing import Queue, Process

def func(exp, q):
    ret = eval(exp)
    q.put(ret) # 储存结果,先进先出。
    
if __name__ == '__main__':
    q = Queue() # 创建对象
    Process(target=func,args=('1+2+3', q)).start() # 传入参数
    print(q.get()) # 获取结果

# Queue内部基于文件家族socket、pickle、Lock实现。
from multiprocessing import Queue

q = Queue()
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
print(q.get()) # 在队列为空的时候会发生阻塞
from multiprocessing import Queue

q = Queue(2) # 设置队列大小
q.put(1)
q.put(2)
print('显示')
q.put(3) # 当队列为满的时候向队列中放数据,会阻塞:不报错且不丢失数据。
print('不显示')
import queue
from multiprocessing import Queue
q = Queue(2)
q.put(1)
q.put(2)
print('显示')
try:
    q.put_nowait(3)  # 当队列为满的时候用该方法放数据,不阻塞:系统会报错且会丢失数据。
except queue.Full: 
    pass
print('不显示')

print(q.get())
print(q.get())
print(q.get()) # 阻塞
import queue
from multiprocessing import Queue
q = Queue(2)
q.put(1)
q.put(2)
print('显示')
try:
    q.put_nowait(3)  
except queue.Full:
    pass
print('不显示')

print(q.get())
print(q.get())
try:
    print(q.get_nowait())  # 队列为空的时候,不阻塞:系统会报错。
except queue.Empty:
    pass

猜你喜欢

转载自www.cnblogs.com/elliottwave/p/12656168.html
33