进程线程不管哪门语言都是一块可以被重视的方向,下面一起学习学习python中的进程,线程
1.进程线程区别
通俗解释:一个程序QQ的运行就是一个进程运行;QQ中打开多个页面互不影响可以同时操作的每个页面的运作就是线程
专业解释:进程-担当系统分配资源(CPU时间,内存)基本单元;线程-程序执行的最小单元
2.进程
1)fork()
2.1.1:介绍
linux可以多进程操作,所以它能实现登录多个QQ;os模块封装了fork()方法能创建一个进程
操作系统会创建一个新的进程,复制父进程所有信息到子进程中;
fork()函数一定会得到一个返回值,子进程中为0,父进程中就是子进程的id号;
父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID;
getpid():放回当前进程标识;getppid():返回父进程标识
2.1.2:用法
import os rpid = os.fork() if rpid<0: print("fork调用失败。") elif rpid == 0: print("我是子进程(%s),我的父进程是(%s)"%(os.getpid(),os.getppid())) x+=1 else: print("我是父进程(%s),我的子进程是(%s)"%(os.getpid(),rpid)) print("父子进程都可以执行这里的代码") 运行结果: 我是父进程(19360),我的子进程是(19361) 父子进程都可以执行这里的代码 我是子进程(19361),我的父进程是(19360) 父子进程都可以执行这里的代码
2)注意
多进程中所有数据(包括全局变量)都各有一份,互不影响
3)multiprocessing
2.3.1:介绍
跨平台的非linux支持的多进程模块,提供了一个Process类代表一个进程对象
2.3.2: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值;
2.3.3 例子
from multiprocessing import Process import os from time import sleep # 子进程要执行的代码 def run_proc(name, age, **kwargs): for i in range(10): print(‘子进程运行中,name= %s,age=%d ,pid=%d...‘ % (name, age,os.getpid())) print(kwargs) sleep(0.5) if __name__==‘__main__‘: print(‘父进程 %d.‘ % os.getpid()) p = Process(target=run_proc, args=(‘test‘,18), kwargs={"m":20}) print(‘子进程将要执行‘) p.start() sleep(1) p.terminate() p.join() print(‘子进程已结束‘)
运行结果: 父进程 21378. 子进程将要执行 子进程运行中,name= test,age=18 ,pid=21379... {‘m‘: 20} 子进程运行中,name= test,age=18 ,pid=21379... {‘m‘: 20} 子进程已结束
2.3.4 Process子类-进程创建
创建新的进程还能够使用类的方式,可以自定义一个类,继承Process类
例子:
from multiprocessing import Process import time import os #继承Process类 class Process_Class(Process): #因为Process类本身也有__init__方法,这个子类相当于重写了这个方法, #但这样就会带来一个问题,我们并没有完全的初始化一个Process类,所以就不能使用从这个类继承的一些方法和属性, #最好的方法就是将继承类本身传递给Process.__init__方法,完成这些初始化操作 def __init__(self,interval): Process.__init__(self) self.interval = interval #重写了Process类的run()方法 def run(self): print("子进程(%s) 开始执行,父进程为(%s)"%(os.getpid(),os.getppid())) t_start = time.time() time.sleep(self.interval) t_stop = time.time() print("(%s)执行结束,耗时%0.2f秒"%(os.getpid(),t_stop-t_start)) if __name__=="__main__": t_start = time.time() print("当前程序进程(%s)"%os.getpid()) p1 = Process_Class(2) #对一个不包含target属性的Process类执行start()方法,就会运行这个类中的run()方法,所以这里会执行p1.run() p1.start() p1.join() t_stop = time.time() print("(%s)执行结束,耗时%0.2f"%(os.getpid(),t_stop-t_start))
4)进程池
当子进程不多,直接用multprocessing中的Process动态生成多个进程,但如果是很多,手动创建工作量很大,此时就可以用multprocessing模块提供的pool方法了
运行机制:初始化进程池时可以指定最大进程数,当有新请求会先看进程池运行进程是否达到最大进程数,没有就创建一个进程放入进程池来维护同时运行子进程
multiprocessing.Pool常用函数解析: apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表; apply(func[, args[, kwds]]):使用阻塞方式调用func close():关闭Pool,使其不再接受新的任务; terminate():不管任务是否完成,立即终止; join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;
例子:
from multiprocessing import Pool import os,time,random def worker(msg): t_start = time.time() print("%s开始执行,进程号为%d"%(msg,os.getpid())) #random.random()随机生成0~1之间的浮点数 time.sleep(random.random()*2) t_stop = time.time() print(msg,"执行完毕,耗时%0.2f"%(t_stop-t_start)) po=Pool(3) #定义一个进程池,最大进程数3 for i in range(0,10): #Pool.apply_async(要调用的目标,(传递给目标的参数元祖,)) #每次循环将会用空闲出来的子进程去调用目标 po.apply_async(worker,(i,)) print("----start----") po.close() #关闭进程池,关闭后po不再接收新的请求 po.join() #等待po中所有子进程执行完成,必须放在close语句之后 print("-----end-----") 运行结果: ----start---- 0开始执行,进程号为21466 1开始执行,进程号为21468 2开始执行,进程号为21467 0 执行完毕,耗时1.01 3开始执行,进程号为21466 2 执行完毕,耗时1.24 4开始执行,进程号为21467 3 执行完毕,耗时0.56 5开始执行,进程号为21466 1 执行完毕,耗时1.68 6开始执行,进程号为21468 4 执行完毕,耗时0.67 7开始执行,进程号为21467 5 执行完毕,耗时0.83 8开始执行,进程号为21466 6 执行完毕,耗时0.75 9开始执行,进程号为21468 7 执行完毕,耗时1.03 8 执行完毕,耗时1.05 9 执行完毕,耗时1.69 -----end-----
5)进程间的通信-Queue
Process之间有时也需要通信,操作系统提供了很多机制来实现进程间通信
2.5.1 Queue方法介绍
初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头); Queue.qsize():返回当前队列包含的消息数量; Queue.empty():如果队列为空,返回True,反之False ; Queue.full():如果队列满了,返回True,反之False; Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;
例子
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 ‘所有数据都写入并且读完‘
2.5.2 进程池中的Queue
如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue()
例子
#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 "dongGe": q.put(i) if __name__=="__main__": print("(%s) start"%os.getpid()) q=Manager().Queue() #使用Manager中的Queue来初始化 po=Pool() #使用阻塞模式创建进程,这样就不需要在reader中使用死循环了,可以让writer完全执行完成后,再用reader去读取 po.apply(writer,(q,)) po.apply(reader,(q,)) po.close() po.join() print("(%s) End"%os.getpid())
2.线程
原文地址:https://www.cnblogs.com/xiaoping1993/p/9709973.html