No.32multiprocessing模块

No.32

今日概要

  • 线程
  • multiprocessing 模块

内容回顾

  1. 操作系统
    • 发展历史
      • 多道操作系统
      • 分时操作系统
      • 实时操作系统
    • 功能
      • 负责调度进程先后执行的顺序
      • 控制进程执行的时间
      • 分配资源
  2. 进程
    • 进程和程序的区别
      • 运行的程序就是一个进程
    • 进程的调度
      • 是由操作系统完成
    • 三状态
      • 就绪
        • 就绪 → system call → 运行
      • 运行
        • 运行 → 时间片到 → 就绪
      • 阻塞
        • 运行 → IO → 阻塞 → IO结束 → 就绪
        • 阻塞影响了程序运行的效率
  3. 同步异步
    • 同步:调用一个方法要等待这个方法结束。
    • 异步:调用一个方法不等待这个方法结束,也不关心这个方法做了什么。
  4. 并发并行
    • 并发:
      • 多个程序共用一个CPU轮流使用
    • 并行:
      • 多个程序,多个CPU
      • 一个CPU上运行一个程序,多个程序同时在多个CPU上运行
  5. 阻塞非阻塞
    • 阻塞:CPU不工作
    • 非阻塞:CPU工作

内容详细

1.线程

  • 线程是进程中的一部分,专门负责执行具体代码。
  • 每一个进程中至少有一个线程。
  • 线程和进程的区分
    • 功能
      • 进程是计算机中资源分配的最小单位(进程负责分配资源)
      • 线程是计算机中被CPU调度的最小单位(线程负责执行代码)
    • 开销
      • 线程的创建、销毁、切换所需时间开销都远远小于进程。
    • 总结
      • 进程:数据隔离 开销大 同时执行几段代码
      • 线程:数据共享 开销小 同时执行几段代码
  • Python中的线程比较特殊,所以进程也可能会被常用到。

2.multiprocessing 模块

import os
import time

print('start')
time.sleep(20)
print(os.getpid(), os.getppid(), 'end')

# pid:process id  子进程id
# ppid:parent process id  父进程id
# 在Pycharm中启动的所有Py程序都是Pycharm的子进程
import os
import time
from multiprocessing import Process

def func():
    print('start', os.getpid())
    time.sleep(1)
    print('end', os.getpid())

if __name__ == '__main__':
    P = Process(target=func)  # 创建一个即将要执行func函数的进程对象
    P.start()  # 异步:调用开启进程的方法,不等待是否真的开启。
    print('main', os.getpid())

结果:
main 12108
start 8628
end 8628
'''
if __name__ == '__main__':
	当这个Py文件当作脚本直接执行的时候,会执行里面的代码。
	当这个Py文件当作模块被导入的时候,不执行里面的代码。

__name__ == '__main__'
	执行的文件就是__name__所在的文件。

__name__ == '文件名'
	 __name__所在的文件被导入执行的时候。
'''

import os
import time
from multiprocessing import Process

def func():
    print('start', os.getpid())
    time.sleep(1)
    print('end', os.getpid())

# windows系统中不写 if __name__ == '__main__': 执行时会报错
P = Process(target=func)
P.start()
print('main', os.getpid())

# 报错原因:操作系统创建的进程方式不同
windows系统中创建新的子进程,子进程会先import父进程的代码,从而完成数据导入。
所以我们只希望在父进程中执行的代码就要写在 if __name__ == '__main__': 下面。

ios/linux系统创建新的子进程,子进程会直接拷贝父进程执行代码后的结果,不会再重新执行一遍父进程的代码。(fork) 
import os
import time
from multiprocessing import Process

def func():
    print('start func', os.getpid())
    time.sleep(1)
    print('end func', os.getpid())

def show():
    print('start show', os.getpid())
    time.sleep(1)
    print('end show', os.getpid())

if __name__ == '__main__':
    p1 = Process(target=func) # 创建一个即将要执行func函数的进程对象
    p1.start() # 开启进程
    p2 = Process(target=show)
    p2.start()
    print('main', os.getpid())

结果:
main 9652
start func 1632
start show 6728
end func 1632
end show 6728
'''
主进程的结束逻辑:
1主进程代码执行结束 → 所有子进程结束 or 所有子进程结束 → 主进程代码执行结束
2给子进程回收资源
3主进程结束
'''

import os
import time
from multiprocessing import Process

def func():
    print('start', os.getpid())
    time.sleep(10)
    print('end', os.getpid())

if __name__ == '__main__':
    P = Process(target=func)
    P.start()
    print('main', os.getpid())

# 主进程代码执行完后并不会立即结束。
# 主进程会等待所有子进程结束后才会结束,因为主进程要负责回收子进程的资源。
# 如果子进程结束,父进程没有回收资源,那么这个子进程会变成僵尸进程。
'''
进程之间数据相互隔离,那么主进程是怎么知道子进程结束的?
基于网路,基于文件(操作系统)。
'''

# join方法:直到子进程结束才会执行后续代码
from multiprocessing improt Process
import time

def send_email():
    time.sleep(3)
    print('发送了一封邮件')
    
if __name__ == '__main__':
	p = Process(target=send_email)
	p.start() # 异步非阻塞
	p.join()  # 同步阻塞:直到p对应的进程结束,才结束阻塞。
	print('邮件发送完毕')
    
结果:
执行发邮件函数的子进程结束后,才会打印。
# 开启10个进程,给公司5000人发邮件;直到发送完所有邮件后,再打印:邮件发送完毕。
import time
import random
from multiprocessing improt Process

def send_mail(i):
    time.sleep(random.random())  # 每个进程睡眠时间随机
    print('发送了一封邮件', i)

if __name__ == '__main__':
    lst = []
    
    for i in range(10):
        p = Process(target=send_mail, args=(i,))  # args传入的是元组
        p.start()
        l.append(p)  # 把每个进程对象都加入列表
    print(l)
    
    for p in lst:
        p.join()  # 阻塞:直到上面的10个进程都结束
    print('5000封邮件发送完毕')

3总结

  • 开启一个进程

    • 函数名(参数1,参数2)
    • from multiprocessing import Process
    • p = Process ( target=函数名,args=(参数1,参数2) )
    • p.start()
  • 父进程和子进程

    • 父进程会等待所有的子进程结束之后才会结束
    • 回收资源
  • windows 和 ios/linux 开启进程的区别

    • windows
      • 开启进程的代码需要放在 if __name__ == '__main__'
        • 因为新开的子进程会先把主进程中的代码从头到尾先执行一遍,这样就会导致子进程连续创建子进程。
    • lios/linux
      • 新开的子进程不会执行主进程中的代码,而是直接拷贝主进程代码执行后的结果。
      • 可以理解成:新开的子进程直接执行调用的函数。
  • join方法

    • 把一个进程的结束事件封装成了一个join方法。

    • 执行join方法的效果就是形成阻塞,直到子进程执行结束就结束。

    • 在多个子进程中使用join

      from multiprocessing improt Process
      
      def 函数名(参数1,参数2)
      	pass
      
      if __name__ == '__main__':
          lst = []
          for i in range(10):
              p = Process(target=函数名,args(参数1,参数2))
              p.start()
              lst.append(p)
          for i in lst:
          	p.join()
      	# 当所有子进程都结束后,需要执行的代码就写在这里。
      

猜你喜欢

转载自www.cnblogs.com/elliottwave/p/12656053.html