[TOC]
# 进程状态
![](https://box.kancloud.cn/12d1215f70c332d1bd4839cd0a9967ed_662x216.png)
* 就绪状态: 运行的条件都已经满足,正在等待cpu执行
* 执行态: cpu正在执行其功能
* 等待态: 等待某些条件满足,例如一个程序sleep了,此时就处于等待态
# fork方式
Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID
~~~
import os
# 表示父进程开始了
print('Process (%s) start...' % os.getpid())
# 只能unix和linux和mac
pid = os.fork()
if pid == 0:
# 子进程
print('我是子进程,进程id是 (%s) , 父进程的id是 (%s)' % (os.getpid(), os.getppid()))
else:
# 父进程
print('我是父进程,进程id是 (%s) , 我创建的子进程id是 (%s) ' % (os.getpid(), pid))
~~~
输出
~~~
Process (32454) start...
我是父进程,进程id是 (32454) , 我创建的子进程id是 (32458)
我是子进程,进程id是 (32458) , 父进程的id是 (32454)
~~~
由于Windows没有fork调用,上面的代码在Windows上无法运行
# multiprocessing
multiprocessing模块就是跨平台版本的多进程模块
multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:
~~~
import multiprocessing
import os
# 子进程要执行的代码
def run_proc(name):
print('子进程的参数name是 (%s) , id是 (%s) ' % (name, os.getpid()))
if __name__ == '__main__':
print('父进程的id是 (%s)' % os.getpid())
p = multiprocessing.Process(target=run_proc, args=('test', ))
print('子进程将要开始了...')
p.start()
p.join()
print('子进程结束...')
~~~
输出
~~~
父进程的id是 (32564)
子进程将要开始了...
子进程的参数name是 (test) , id是 (32568)
子进程结束...
~~~
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。
join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步
# Pool进程池
如果要启动大量的子进程,可以用进程池的方式批量创建子进程
~~~
import multiprocessing
import os, time, random
def long_time_task(name):
print('参数name:(%s),子进程id是(%s)' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('参数name:(%s),运行了 %0.2f 秒' % (name, (end - start)))
if __name__ == '__main__':
print('父进程的id是 (%s)' % os.getpid())
# 创建4个子进程
p = multiprocessing.Pool(4)
# 循环5次,投递5个任务给这些子进程
for i in range(5):
p.apply_async(long_time_task, args=(i, ))
print('等待所有进程结束')
# 关闭进程池,关闭后不再接收新的请求
p.close()
# 等待po中所有子进程执行完成,必须放在close之后
p.join()
print('所有进程结束')
~~~
输出
~~~
父进程的id是 (35744)
等待所有进程结束
参数name:(0),子进程id是(35749)
参数name:(1),子进程id是(35750)
参数name:(2),子进程id是(35751)
参数name:(3),子进程id是(35752)
参数name:(2),运行了 1.57 秒
参数name:(4),子进程id是(35751)
参数name:(3),运行了 2.22 秒
参数name:(0),运行了 2.76 秒
参数name:(1),运行了 2.94 秒
参数name:(4),运行了 2.54 秒
所有进程结束
~~~
代码解读:
对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。
请注意输出的结果,task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4,因此,最多同时执行4个进程。这是Pool有意设计的限制,并不是操作系统的限制。如果改成
# 控制子进程输入和输出
很多时候,子进程并不是自身,而是一个外部进程。我们创建了子进程后,还需要控制子进程的输入和输出。
subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出
下面的例子演示了如何在Python代码中运行命令`nslookup www.python.org`(查询域名的DNS解析),这和命令行直接运行的效果是一样的
~~~
import subprocess
print('$ nslookup www.python.org')
r = subprocess.call(['nslookup', 'www.python.org'])
print('结束的状态码:', r)
~~~
输出
~~~
$ nslookup www.python.org
Server: 192.168.3.1
Address: 192.168.3.1#53
Non-authoritative answer:
www.python.org canonical name = dualstack.python.map.fastly.net.
Name: dualstack.python.map.fastly.net
Address: 151.101.108.223
结束的状态码: 0
~~~
如果子进程还需要输入,则可以通过communicate()方法输入
~~~
import subprocess
print('$ nslookup')
p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate(b'set q=mx\npython.org\nexit\n')
print(output.decode('utf-8'))
print('Exit code:', p.returncode)
~~~
上面的代码相当于在命令行执行命令nslookup,然后手动输入
~~~
set q=mx
python.org
exit
~~~
运行结果如下:
~~~
$ nslookup
Server: 192.168.19.4
Address: 192.168.19.4#53
Non-authoritative answer:
python.org mail exchanger = 50 mail.python.org.
Authoritative answers can be found from:
mail.python.org internet address = 82.94.164.166
mail.python.org has AAAA address 2001:888:2000:d::a6
Exit code: 0
~~~
# 进程间通信
Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
**常用方法**
~~~
# 创建队列,可以指定容量
q = multiprocessing.Queue()
# 队列是不是满的
q.full()
# 获取数据
q.get()
# 队列是不是空
q.empty()
# 写入数据
q.put(xx)
~~~
我们以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据
~~~
import multiprocessing
import os, time, random
# 写数据进程执行的代码
def write(q):
print('进程id (%s) 正在写入' % os.getpid())
for value in ['A', 'B', 'C']:
print('把(%s)放到队列...' % value)
q.put(value)
time.sleep(random.random())
# 读数据进程执行的代码
def read(q):
print('进程id (%s) 正在读取' % os.getpid())
while True:
value = q.get(True)
print('获取(%s)从队列中' % value)
if __name__ == '__main__':
# 父进程创建queue,并传给各个子进程
q = multiprocessing.Queue()
pw = multiprocessing.Process(target=write, args=(q, ))
pr = multiprocessing.Process(target=read, args=(q, ))
# 启动子进程pw,写入:
pw.start()
# 启动子进程pr,写入:
pr.start()
# 等待pw结束
pw.join()
# pr进程里是死循环,无法等待其结束,只能强行终止
pr.terminate()
~~~
输出
~~~
进程id (36109) 正在写入
把(A)放到队列...
进程id (36110) 正在读取
获取(A)从队列中
把(B)放到队列...
获取(B)从队列中
把(C)放到队列...
获取(C)从队列中
~~~
在Unix/Linux下,multiprocessing模块封装了fork()调用,使我们不需要关注fork()的细节。由于Windows没有fork调用,因此,multiprocessing需要“模拟”出fork的效果,父进程所有Python对象都必须通过pickle序列化再传到子进程去,所有,如果multiprocessing在Windows下调用失败了,要先考虑是不是pickle失败了
- python入门
- 软件安装
- anaconda使用
- py解释器
- 数据类型和变量
- 编码
- 字符串
- 格式化
- 数据类型
- 运算符
- list和tuple
- 列表生成式
- dict和set
- 切片和迭代
- set,list,tuple之间互换
- is和==
- 公共方法
- 反射操作
- 数学运算
- 类型转换
- 对象操作
- 序列操作
- 运算符
- 内置函数
- 交互操作
- 编译执行
- 引用
- 判断,循环
- 生成器
- 迭代器
- 函数
- 数据类型转换
- 空函数
- 参数
- 全局变量
- 返回值
- 递归
- 匿名函数
- 文件操作
- 打开和关闭
- 读写
- 备份文件
- 文件定位读写
- 重命名,删除
- 文件夹相关操作
- with
- StringIO和BytesIO
- 操作文件和目录
- 序列化
- 文件属性
- 面向对象
- 类和对象
- init()方法
- 魔法方法
- 继承
- 重写
- 多态
- 类属性,实例属性
- 静态方法和类方法
- 工厂模式
- 单例模式
- 异常
- 私有化
- 获取对象信息
- *args和**kwargs
- property属性
- 元类
- slots
- 定制类
- 枚举
- 模块
- 模块介绍
- 模块中的__name__
- 模块中的__all__
- 包
- 模块发布
- 模块的安装和使用
- 多模块开发
- 标准库
- 给程序传参数
- 时间
- 正则表达式
- GIL
- 深拷贝和浅拷贝
- 单元测试
- pyqt
- 安装
- 设置窗口图标和移动窗口
- 设置气泡提示和文本
- 图片展示
- 文本框控件
- 按钮控件
- 信号和槽
- 布局
- 对话框控件
- pygame
- 窗体关闭事件
- 显示图片
- 移动图片
- 文本显示
- 背景音和音效
- FPS计算
- surface
- 鼠标事件
- 函数式编程
- map/reduce
- filter
- sorted
- 返回函数
- 装饰器
- 偏函数
- 网络编程
- tcp
- udp
- socket
- epoll
- WSGI
- 多任务
- 多线程
- 多进程
- 分布式进程
- 协程
- 迭代器
- 生成器
- yield多任务
- greenlet
- gevent
- ThreadLocal
- asyncio
- async/await
- aiohttp
- 常用内建模块
- datetime
- collections
- base64
- struct
- hashlib
- hmac
- itertools
- urllib
- xml
- HTMLParser
- 常用第三方模块
- pillow
- requests
- chardet
- psutil
- 图形界面
- 海龟绘图
- Django
- 虚拟环境搭建
- ORM
- 模型类设计和表生成
- 模型类操作
- 关系查询
- 后台管理
- 配置mysql
- 字段属性和选项
- 查询
- 模型关联
- 路由
- 模板
- selenium
- 基本原理
- api
- 八种定位方式
- 元素的操作
- 多标签
- 多表单
- 鼠标,键盘
- 警告框
- 下拉框
- 执行js
- 等待
- cookie
- 封装
- unittest模块
- 断言
- 测试用例
- jmeter
- jmeter简介
- jmeter提取json
- 添加header和cookie
- 读取csv/txt文件
- 配置文件
- ant