进程
进程号和端口号有什么关系?
每个进程都有进程号,但不一定都端口号
进程相关命令(linux平台)
ps aux | 查看所有进程 |
ps aux | grep 进程名称 | 查看指定的进程 |
kill 进程号 | 杀死一个进程 |
htop | 动态的显示进程 |
进程的创建
1,创建进程的第一种方式 Process
1,提供子进程执行的任务(或者叫函数)
2,创建进程对象(用Process()),参数target后面直接写函数名,不要带小括号
3,开启进程 ( 进程对象.start() )
2,创建进程的第二种方式 进程池
2.1,进程池的使用方式
1,准备一个空的进程池
2,每有一个新的任务就开启一个新的进程,直到池子被沾满
3,每当一个任务完成后,进程不会被销毁,而是从任务队列取出一个任务执行
2.2,进程池的创建步骤
导入模块: from multiprocessing import Pool
创建对象: p = Pool(3) 定义一个进程池,进程最大数量为3 如果不写数字,就是无上限;创建进程池的目的,就是限制进程的数量,如果不写数字,那么创建进程池就没有什么意义
添加任务:p.apply_async(函数名,元组(位置参数),字典(关键字参数)) p.apply_asyic(worker, (i,), {} )
由于主进程不会等待 进程池 结束,所以后面还必须添加两行代码:
p.close() 关闭进程池的任务队列入口,关闭后不再接受新的任务请求
p.join() 使主进程等待进程池中的所有子进程执行完成,才结束;注意必须放在close语句后
2.3,进程池使用队列Queue
from multiprocessing import Manager
q = Manager().Queue()
如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到一条如下的错误信息:
RuntimeError: Queue objects should only be shared between processes through inheritance.
下面代码演示了进程池中的通信:
# -*- coding:utf-8 -*-
# 修改import中的Queue为Manager
from multiprocessing import Manager,Pool
import os,time,random
def reader(q):
print("reader启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
for i in range(q.qsize()):
print("reader从Queue获取到消息:%s" % q.get(True))
def writer(q):
print("writer启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
for i in "itcast":
q.put(i)
if __name__=="__main__":
print("(%s) start" % os.getpid())
q = Manager().Queue() # 使用Manager中的Queue
po = Pool()
po.apply_async(writer, (q,))
time.sleep(1) # 先让上面的任务向Queue存入数据,然后再让下面的任务开始从中取数据
po.apply_async(reader, (q,))
po.close()
po.join()
print("(%s) End" % os.getpid())
运行结果:
(4044) start
writer启动(4088),父进程为(4044)
reader启动(4088),父进程为(4044)
reader从Queue获取到消息:i
reader从Queue获取到消息:t
reader从Queue获取到消息:c
reader从Queue获取到消息:a
reader从Queue获取到消息:s
reader从Queue获取到消息:t
(4044) End
3,其他
3.1 进程的执行原理
简要过程:先开启主进程,主进程里面开启子进程,子进程里面只执行指定的函数
进程特点:
1,创建子进程时,会复制启动时主进程的所有数据
2,主进程启动时会从文件的第一行开始执行,子进程启动时会从指定的函数开始执行
3.2 主进程与子进程的关系
1,子进程代码结束之后,会自动销毁
2,所有的子进程都死亡,主进程才会死亡
3,一旦主进程死亡,所有的子进程都会死亡
3.3 启动进程时传递参数
args 传 位置参数, kwargs 传 关键字参数
具体步骤:
multiprocessing.Process(target=函数名 ,args=元组, kwargs=字典)
3.4 进程和线程的区别
进程: 进程是系统分配资源的一个单位,不仅仅包含线程,还包含数据,一个进程里面可以有多个线程
线程:线程只负责抢时间片,线程没有数据,数据都存在进程里
总结:
1,一个程序至少有一个进程,一个进程至少有一个线程
2,进程用于封装资源,线程用于处理cpu时间片的竞争
3,进程间不能共享数据,线程可以共享数据(原因:如果一个进程里面开启多个线程,线程之间共享进程里面的数据)
4,开发多任务,想要保证数据安全就用进程,如果想节约资源就用线程;想要数据独立就用进程,如果只想开启多任务,数据独不独立无所谓,就用线程
5,进程与线程相比,进程占用的的资源更多(一个进程里面不但有主线程程,还可以开启多个子线程,还有数据(变量))
3.5 进程间怎么通信?
搞一个共享内存,往里面存数据
消息队列Queue
1,Queue的介绍及特点
队列 | 先进先出 |
栈 | 先进后出/后进先出 |
2,Queue的基本使用
导入模块:from multiprocessing import Queue
创建对象:q = Queue()
添加数据:q.put() 堵塞
获取数据:q.get() 堵塞
添加数据:q.put_nowait() 不堵塞 异常名:queue.Full
获取数据:q.get_nowait() 不堵塞 异常名:queue.Empty
代码实例演示:
#coding=utf-8
from multiprocessing import Queue
q=Queue(3) #初始化一个Queue对象,最多可接收三条put消息
q.put("消息1")
q.put("消息2")
print(q.full()) #False
q.put("消息3")
print(q.full()) #True
#因为消息列队已满下面的try都会抛出异常,第一个try会等待2秒后再抛出异常,第二个Try会立刻抛出异常
try:
q.put("消息4",True,2)
except:
print("消息列队已满,现有消息数量:%s"%q.qsize())
try:
q.put_nowait("消息4")
except:
print("消息列队已满,现有消息数量:%s"%q.qsize())
#推荐的方式,先判断消息列队是否已满,再写入
if not q.full():
q.put_nowait("消息4")
#读取消息时,先判断消息列队是否为空,再读取
if not q.empty():
for i in range(q.qsize()):
print(q.get_nowait())
运行结果:
False
True
消息列队已满,现有消息数量:3
消息列队已满,现有消息数量:3
消息1
消息2
消息3