Python并发编程之进程

发表于:2018-4-02 10:46

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:曹艳飞    来源:博客园

  一、理论概念
  1、定义
  进程(Process 也可以称为重量级进程)是程序的一次执行。在每个进程中都有自己的地址空间、内存、数据栈以及记录运行的辅助数据,它是系统进行资源分配和调度的一个独立单位。
  2、并行和并发
  并行:并行是指多个任务同一时间执行;
  并发:是指在资源有限的情况下,两个任务相互交替着使用资源;
  3、同步和异常
  同步是指多个任务在执行时有一个先后的顺序,必须是一个任务执行完成另外一个任务才能执行;
  异步是指多个任务在执行时没有先后顺序,多个任务可以同时执行;
  4、同步/异步/阻塞/非阻塞/
  同步阻塞:这个阻塞的形成效率是最低的;比如你在下载一个东西是,你一直盯着下载进度条,到达100%时下载完成;
  同步体现在:你等待下载完成通知;
  阻塞体现在:等待下载的过程中,不能做别的事情
  同步非阻塞:你在下载东西时,你把任务提交后就去干别的事情了,只是每过一段时间就看一下是不是下载完成;
  同步体现在:等待下载完成通知;
  非阻塞提现在:等待下载完成通知过程中,去干别的事情了,只是时不时会瞄一眼进度条;
  异步阻塞:你在下载东西时换了一个现在使用的客户端比如迅雷,下载完成后会有一个提示音,但是这时候你仍然一直在等待那个完成后的提示音;
  异步体现在:下载完成时有提示音;
  阻塞体现在:等待下载完成提示音时,不做任何事情;
  异步非阻塞:你然然使用的是迅雷下载软件,这时候你把下载任务提交后就去干别的事情去了,当你听到‘叮’以后就知道下载完成;
  异步体现在:下载完成叮一声完成通知
  非阻塞体现在:等待下载完成“叮”一声通知过程中,去干别的任务了,只需要接收“叮”声通知即可;
  二、进程的创建与结束
  multiprocessing模块:multiprocess不是一个模块而是python中一个操作、管理进程的包。 之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块。由于提供的子模块非常多,为了方便大家归类记忆,我将这部分大致分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。
  Process模块的各种方法介绍
  Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
  强调:
  1. 需要使用关键字的方式来指定参数
  2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
  参数介绍:
  group参数未使用,值始终为None
  target表示调用对象,即子进程要执行的任务
  args表示调用对象的位置参数元组,args=(1,2,'egon',)
  kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
  name为子进程的名称
  p.start():启动进程,并调用该子进程中的p.run()
  p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
  p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
  p.is_alive():如果p仍然运行,返回True
  p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
  windows中使用process注意事项:
  在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ ==‘__main__’ 判断保护起来,import 的时候 ,就不会递归运行了。
  process模块创建进程:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author: caoyf
import  time
from multiprocessing import  Process
def func(name):
print('hello %s'%name)
print('我是子进程')
if __name__ == '__main__':
p = Process(target=func,args=('caoyf',))  #在实例化时候,args的参数必须是一个元祖形式(注册一个子进程)
p.start() #启动一个子进程
time.sleep(3)
print('执行主进程内容了')
  创建第一个进程
  多个进程同时运行,子进程的执行顺序不是根据启动的顺序来决定的;
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author: caoyf
import  time
from multiprocessing import  Process
def func(name):
print('hello %s'%name)
time.sleep(2)
if __name__ == '__main__':
p_lst =  []
for i in range(10):
p = Process(target=func, args=('caoyf',))
p.start()
p_lst.append(p)
for p in p_lst: p.join()  # 是感知一个子进程的结束,将异步的程序改为同步
print('父进程在运行')
  多个进程同时运行
  另一种开启进程的方法,继承process的形式
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author: caoyf
import  time
import os
from multiprocessing import  Process
class Func(Process):
def __init__(self,name):
super().__init__()
self.name = name
def run(self):
print(os.getpid())
print('%s正在和小明聊天'%self.name)
if __name__ == '__main__':
p1 = Func('caoyf')
p2 = Func('Zhao')
p1.start()
p2.start()
p1.join()
p2.join()
  继承的方式开启进程
  守护进程:会随着主进程的结束而结束,进程之间是相互独立的,主进程的代码运行结束,守护进程也会随即结束
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author: caoyf
import  time
import os
from multiprocessing import  Process
def foo():
print('start123')
time.sleep(2)
print('end123')
def func():
print('start456')
time.sleep(5)
print('end456')
if __name__ == '__main__':
p1 = Process(target=foo)
p2 = Process(target=func)
p1.daemon = True
p1.start()
p2.start()
time.sleep(0.1)
print('main------------')#打印该行则主进程代码结束,则守护进程p1应该被终止.#可能会有p1任务执行的打印信息123,因为主进程打印main---
# -时,p1也执行了,但是随即被终止.
  守护进程
  三、进程同步(multiprocessing.Lock\Spemaphore\Event)
  锁(Lock):
  资源是有限的,多个进程如果对同一个对象进行操作,则有可能造成资源的争用,甚至导致死锁,在并发进程中就可以利用锁进行操作来避免访问的冲突;
  加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,但是速度就变慢了,但牺牲了速度却保证了数据安全。
  虽然可以用文件共享数据实现进程间通信,但问题是:
  1.效率低(共享数据基于文件,而文件是硬盘上的数据)
  2.需要自己加锁处理
  我们可以模拟一个火车抢票的过程,当过个客户同时对一个程序发起访问时,假设此时有5张票,有10个人抢
from multiprocessing import Process,Lock
import time,json,random
def search():
dic=json.load(open('db'))
print('\033[43m剩余票数%s\033[0m' %dic['count'])
def get():
dic=json.load(open('db'))
time.sleep(random.random()) #模拟读数据的网络延迟
if dic['count'] >0:
dic['count']-=1
time.sleep(random.random()) #模拟写数据的网络延迟
json.dump(dic,open('db','w'))
print('\033[32m购票成功\033[0m')
else:
print('\033[31m购票失败\033[0m')
def task(lock):
search()
lock.acquire()
get()
lock.release()
if __name__ == '__main__':
lock = Lock()
for i in range(100): #模拟并发100个客户端抢票
p=Process(target=task,args=(lock,))
p.start()
  抢火车票
  信号量:
  信号量Semaphore是同时允许一定数量的线程更改数据 。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author: caoyf
import  time
import  random
from multiprocessing import Semaphore
from multiprocessing import Process
def f(i,a):
a.acquire()
print('%s走进了房间'%i)
time.sleep(random.randint(1,5))
print('%s走出了房间'%i)
a.release()
if __name__ == '__main__':
a = Semaphore(5)
for i in range(10):
p = Process(target=f,args=(i,a))
p.start()
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号