并发:一个处理器同时处理多个任务。
并行:多个处理器或者是多核的处理器同时处理多个不同的任务.
fork创建子进程
import os import time #fork出一个子进程,子进程也从这一行开始执行 ret = os.fork() if ret == 0: while True: print("---1---") time.sleep(1) else: while True: print("---2---") time.sleep(1)
输出
---2--- ---1--- ---1--- ---2--- ---2--- ---1--- ---1--- ---2--- ...
查看子进程id
import os import time ret = os.fork() if ret != 0: print("---父进程---%d---"%os.getpid()) else: #ret等于0的是子进程 print("---子进程---%d---父进程---%d---"%(os.getpid(), os.getppid()))
输出
---父进程---5142--- ---子进程---5143---父进程---5142---
全局变量在多个进程之间不共享
import os import time g_num = 100 ret = os.fork() if ret == 0: print("---process-1---") g_num += 1 print("---process-1 g_num=%d---"%g_num) else: time.sleep(3) print("---process-2---") print("---process-2 g_num=%d---"%g_num)
输出
---process-1--- ---process-1 g_num=101--- ---process-2--- ---process-2 g_num=100---
多个fork
import os import time ret = os.fork() if ret != 0: print("---1---") else: print("---2---") ret = os.fork() if ret != 0: print("---11---") else: print("---22---")
输出
---1--- ---2--- ---11--- ---22--- ---22--- ---11---
fork炸弹
import os while True: os.fork() print("---1---")
注意:
fork不能在windows下运行
fork创建的主进程不会等到子进程运行结束后再推出
process
Process语法结构如下:
Process([group[,target[,name[,args[,kwargs]]]]])
target:表示这个进程实例所调用对象;
args:表示调用对象的位置参数元组;
kwargs:表示调用对象的关键字参数字典;
name:为当前进程实例的别名;
group:大多数情况下用不到;
Process类常用方法:
is_alive():判断进程实例是否还在执⾏;
join([timeout]):是否等待进程实例执行结束,或等待多少秒;
start():启动进程实例(创建子进程);
run():如果没有给定target参数,对这个对象调用start()方法时,就将执 ⾏对象中的run()⽅法;
terminate():不管任务是否完成,立即终止;
Process类常用属性:
name:当前进程实例别名,默认为Process-N,N为从1开始递增的整数;
pid:当前进程实例的PID值;
process创建子进程
process可以在windows上运行
from multiprocessing import Process import time def test(): while True: print("---test---") time.sleep(1) if __name__ == "__main__": p = Process(target=test) p.start() while True: print("---main---") time.sleep(1)
输出
---main--- ---test--- ---main--- ---test--- ...
创建的子进程和主进程结束
from multiprocessing import Process import time def test(): for i in range(5): print("---test---") time.sleep(1) if __name__ == "__main__": p = Process(target=test) p.start() #让这个进程开始执行test函数里的代码 print("---main---")
输出
---main--- ---test--- ---test--- ---test--- ---test--- ---test---
给target函数传参数
from multiprocessing import Process import os def test(num): print("pid=%d,ppid=%d,num=%d"%(os.getpid(),os.getppid(),num)) if __name__ == "__main__": p = Process(target=test, args=(100,)) p.start() print("---main-pid=%d---"%os.getpid())
输出
---main-pid=14252--- pid=18284,ppid=14252,num=100
join
from multiprocessing import Process import time def test(): for i in range(5): print("---%d---"%i) time.sleep(1) if __name__ == "__main__": p = Process(target=test) p.start() p.join()#阻塞,子进程运行结束后,才向下继续执行 print("---main---")
输出
---0--- ---1--- ---2--- ---3--- ---4--- ---main---
第二种process创建子进程的方法
from multiprocessing import Process import time class MyNewProcess(Process): def run(self): while True: print("---1---") time.sleep(1) if __name__ == "__main__": p = MyNewProcess() p.start() while True: print("---main---") time.sleep(1)
输出
---main--- ---1--- ---main--- ---1--- ---main--- ---1--- ---main--- ---1--- ...
进程池
from multiprocessing import Pool import os import time def test(num): print("pid=%d,ppid=%d,num=%d"%(os.getpid(),os.getppid(),num)) time.sleep(1) if __name__ == "__main__": pool = Pool(3) for i in range(10): print("---%d---"%i) pool.apply_async(test,(i,)) pool.close() pool.join() #join保证子进程运行结束后,进程池才退出
进程间通信-Queue
Process之间有时需要通信,操作系统提供了很多机制来实现进程间的通信。
可以使⽤multiprocessing模块的Queue实现多进程之间的数据传递,Queue 本身是⼀个消息列队程序。
初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最⼤可接收 的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到 内存的尽头);
Queue.qsize():返回当前队列包含的消息数量;
Queue.empty():如果队列为空,返回True,反之False ;
Queue.full():如果队列满了,返回True,反之False;
Queue.get([block[, timeout]]):获取队列中的⼀条消息,然后将其从列队 中移除,block默认值为True;
- 1)如果block使⽤默认值,且没有设置timeout(单位秒),消息列队如果为 空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为⽌, 如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛 出"Queue.Empty"异常;
- 2)如果block值为False,消息列队如果为空,则会⽴刻抛 出"Queue.Empty"异常;
Queue.get_nowait():相当Queue.get(False);
Queue.put(item,[block[, timeout]]):将item消息写⼊队列,block默认值 为True;
- 1)如果block使⽤默认值,且没有设置timeout(单位秒),消息列队如果已 经没有空间可写⼊,此时程序将被阻塞(停在写⼊状态),直到从消息列队 腾出空间为⽌,如果设置了timeout,则会等待timeout秒,若还没空间,则抛 出"Queue.Full"异常;
- 2)如果block值为False,消息列队如果没有空间可写⼊,则会⽴刻抛 出"Queue.Full"异常;
Queue.put_nowait(item):相当Queue.put(item, False);
实例一
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
实例二
from multiprocessing import Process, Queue import os, time, random # 写数据进程执行的代码: def write(q): for value in ['A', 'B', 'C']: print('Put %s to queue...' % value) q.put(value) time.sleep(random.random()) # 读数据进程执行的代码: def read(q): while True: if not q.empty(): value = q.get(True) print('Get %s from queue.' % value) time.sleep(random.random()) else: break if __name__ == '__main__': # 父进程创建Queue,并传给各个子进程: q = Queue() pw = Process(target=write, args=(q,)) pr = Process(target=read, args=(q,)) # 启动子进程pw,写入: pw.start() # 等待pw结束: pw.join() # 启动子进程pr,读取: pr.start() pr.join() # pr进程里是死循环,无法等待其结束,只能强行终止: print("") print("所有数据都写入并且读完")
输出
Put A to queue... Put B to queue... Put C to queue... Get A from queue. Get B from queue. Get C from queue. 所有数据都写入并且读完
进程池中的Queue
如果要使⽤Pool创建进程,就需要使⽤multiprocessing.Manager()中的 Queue(),⽽不是multiprocessing.Queue(),否则会得到⼀条如下的错误信息:
# 修改import中的Queue为Manager from multiprocessing import Manager, Pool import os 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 "dongGe": q.put(i) if __name__ == "__main__": print("(%s) start " % os.getpid()) q = Manager().Queue() # 使用Manager中的Queue来初始化 po = Pool() # 使用阻塞模式创建进程,这样就不需要在reader中使用死循环了,可以让writer完全执行完 po.apply(writer, (q,)) po.apply(reader, (q,)) po.close() po.join() print("(%s) End" % os.getpid())
输出
(15004) start writer启动(10232),父进程为(15004) reader启动(1612),父进程为(15004) reader从Queue获取到消息:d reader从Queue获取到消息:o reader从Queue获取到消息:n reader从Queue获取到消息:g reader从Queue获取到消息:G reader从Queue获取到消息:e (15004) End