多线程
multiprocessing
如果你打算编写多进程的服务程序,python提供一个跨平台的多进程支持,multiprocessing模块就是跨平台版本的多进程模块。
multiprocessing模块提供了一个process类来代表一个进程对象
创建格式1:p = Process(target=函数名,args=( 函数的参数元组 ) )
启动格式:进程对象名.start()
多进程 Process
1 #导入多进程模块 2 from multiprocessing import Process 3 #导入时间模块 4 import time 5 6 def sing(t,msg): 7 #range(开始数值,结果数值),此函数是一组从开始数值到结果数值的数组 8 for i in range(t,msg): 9 print('我唱的第%d首歌'%i) 10 11 def dace(t,msg): 12 for i in range(t,msg): 13 print('我跳的第%d支舞'%i) 14 15 #__name__是当前程序的名称,__main__是运行到前台的名称,即如果当程程序运程到前台 16 if __name__=="__main__": 17 print('启动一个唱歌的进程') 18 # 创建进程 19 #创建格式:p = Process(target=函数名,args=( 函数的参数元组 ) ) 20 p=Process(target=sing,args=(5,10)) 21 p1=Process(target=dace,args=(5,10)) 22 #延时2秒 23 time.sleep(2) 24 #进程对象名.start() 25 p.start() 26 p1.start() 27 ''' 28 输出结果: 29 启动一个唱歌的进程 30 我唱的第5首歌 31 我唱的第6首歌 32 我唱的第7首歌 33 我唱的第8首歌 34 我唱的第9首歌 35 我跳的第5支舞 36 我跳的第6支舞 37 我跳的第7支舞 38 我跳的第8支舞 39 我跳的第9支舞 40 '''
创建格式2:p = Process(target=函数名,name='对象p的自定义名称',args=( 函数的参数元组 ),kwargs=函数的参数{ 字典 } )
1 from multiprocessing import Process 2 import time 3 4 def download(g,g2,**kwargs): 5 dice=kwargs['手机'] 6 if dice=='安卓': 7 print('开始下载游戏%s,%s'%(g,g2)) 8 time.sleep(5) 9 print('游戏下载完成') 10 else: 11 print('设备不匹配') 12 13 if __name__=="__main__": 14 p=Process(target=download,name='pro',args=('跳一跳','跳二跳'),kwargs={'手机':'安卓'}) 15 p.start() 16 17 # time.sleep(5) 18 p.join(6) #阻塞 实现进程间的同步 ,等待子进程执行结束在继续往下执行 19 if p.is_alive()==True: 20 p.terminate() # 杀掉子进程 21 print('下载进程 %s 结束'%p.name) 22 ''' 23 输出结果: 24 开始下载游戏跳一跳,跳二跳 25 游戏下载完成 26 下载进程 pro 结束 27 '''
调用系统多进程类,及主进程,父进程,子进程的认识
1 #导入多线程模块 2 from multiprocessing import Process 3 #导入系统模块 4 import os 5 import time 6 7 8 #创建一个类,它的父类是Process 9 class ClassProcess(Process): 10 # 初始化 11 def __init__(self,num): 12 # 初始化父类的__init__魔术方法 13 Process.__init__(self) 14 #将传入的参数赋给父类Process的__init__,这样父类也就得到传入的参数 15 self.num=num 16 17 def run(self):#run函数是固定函数,在p.start()执行后自动执行 18 #在进程的类执行os.getpid获取子进程,os.getppid()获取父进程 19 print('启动子进程%d,父进程%d'%(os.getpid(),os.getppid()),'参数是%s'%self.num) 20 21 # 主进程的入口 22 if __name__=="__main__": 23 #在主程主执行os.getpid获取主进程 24 print('启动主进程%d'%os.getpid()) 25 # 实例化自定义的类 26 p=ClassProcess('这是参数') 27 # 对于不包含target属性的Process类,执行start方法的时候会自动调用Process类中的run方法 28 p.start() 29 time.sleep(5) 30 p.join(3) 31 print('进程执行完毕')
实例:
1 from multiprocessing import Process 2 3 def sum(msg): 4 j=0 5 for i in range(1,msg+1): 6 j+=i 7 print('%d的累加和是%d'%(msg,j)) 8 9 if __name__=="__main__": 10 num=input('请输入一个整数:') 11 num=int(num) 12 13 # 启动两个进程 14 p1=Process(target=sum,args=(num,)) 15 # p2=ClassProcess(num) 16 p1.start() 17 #等待进程完成再继续执下下面程序 18 p1.join(10) 19 print('都计算完毕')
进程池:
当需要创建的子进程数量不多时,可以直接利用 multiprocessing 中的Process 动态生成多个进程,但是如果是上百个目标,手动的去创建进程工作量巨大,此时就可以使用 multiprocessing 模块提供的 Pool 方法。
初始化 Pool 时,可以指定一个最大进程数,当有新的请求提交到 Pool 中时,如果池还没有满,那么就会创建一个新的进程来执行请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池
中有进程结束,才会创建新的进程来执行。
1 #导入进程池模块 2 from multiprocessing import Pool 3 import time 4 #创建一个函数 5 def dd(msg): 6 s_start=time.time() 7 print('开始执行进程%d'%msg) 8 time.sleep(2) 9 s_end=time.time() 10 print('进程%d执行结束'%msg) 11 if __name__=="__main__": 12 # 创建一个进程池 指定池子大小(最多能存放多少个进程) 13 p=Pool(3) 14 for i in range(6): 15 #进程池异步执行函数 16 # func=函数 17 p.apply_async(func=dd(i),args=(i,))#p.apply_async 异步执行 18 p.close() # 关闭进程池 19 p.join()
进程间的通讯-Queue
process之间有时需要进行通信,系统提供了很多机制来实现进程间的通信
初始化Queue() 对象时(例如:q=Queue()),若括号中没有指定最大可以接收的消息量,那么代表可接受无限
Queue.qsize():返回当前队列包含的消息数量,
Queue.empty():如果队列为空返回True,反之返回False。
Queue.full():如果队列清满了返回True,返之返回False。
Queue.put ( item , [ block [ , timeout ] ] ) :将item消息写入队列,block默认值为True
Queue.put_nowait():Queueput ( block = False)不等待写入消息
Queue.get ( [ block [ , timeout ] ] ) :获取队列中的一条消息,然后将其从列队中移除,block默认值为True。
Queue.get_nowait():是当于Queue.get ( block = False )不等待获取消息
1 from multiprocessing import Queue 2 3 if __name__=="__main__": 4 # 实例化一个消息队列 5 q=Queue(3) # 队列最多只能存储三个消息 6 #--------------------------------------------------------------------- 7 #存消息 q.put() 8 q.put('消息1') 9 q.put('消息2') 10 print(q.full()) # False 没有存满 11 #输出结果:False 12 q.put('消息3') # q.put('消息3',block=True,timeout=None) 13 # --------------------------------------------------------------------- 14 try: 15 # timeout=5等待5秒,5秒内block状态是True,5秒后是False(不等待直接加载消息) 16 q.put('消息4',block=True,timeout=5) 17 except: 18 print('消息队列满了') 19 # 输出结果:消息队列满了 20 # 判断消息队列是否存满 q.full() True 代表存满了 21 print(q.full()) 22 #输出结果:True 23 # --------------------------------------------------------------------- 24 25 # 判断有没有存满 如果没存满 就继续存 26 if not q.full(): 27 q.put('消息5') 28 else: 29 print('已经满了,等待被人读取') 30 # 输出结果:已经满了,等待被人读取 31 # -------------------------------------------------------------------- 32 33 #q.get()读取消息,q.get_nowait()不等待读取消息 34 print(q.get()) 35 # 输出结果:消息1 36 print(q.get_nowait()) 37 # 输出结果:消息2 38 #不等待添加数据 39 if not q.full(): 40 q.put_nowait('消息4')#q.put_nowait不等待存消息 41 #不等待读取 42 print(q.empty()) # 有东西的时候返回 False 43 # 输出结果:False 44 if not q.empty(): 45 print(q.get_nowait()) 46 # 输出结果:消息3
Queue实例
1 from multiprocessing import Queue,Process 2 import time 3 # 写的处理程序 4 def write(q): 5 for i in ['A','B','C']: 6 print('开始写入%s'%i) 7 q.put(i) 8 time.sleep(2) 9 print('%s写入成功'%i) 10 # 读的处理程序 11 def read(q): 12 while True: 13 if not q.empty(): 14 for i in range(q.qsize()): 15 #q.get() 16 print('读出消息%s'%q.get()) 17 else: 18 break 19 20 if __name__=="__main__": 21 q=Queue() 22 23 # 创建两个子进程 24 # 写的子进程 25 qw=Process(target=write(q),args=(q,)) 26 qw.start() 27 qw.join() 28 # 读的子进程 29 qr=Process(target=read(q),args=(q,)) 30 qr.start() 31 qr.join() 32 33 ''' 34 输出结果: 35 开始写入A 36 A写入成功 37 开始写入B 38 B写入成功 39 开始写入C 40 C写入成功 41 读出消息A 42 读出消息B 43 读出消息C 44 '''
多线程-threading
多任务可以由多进程完成,也可以由一个进程内的多线程完成。
我们前面提到了进程是由若干个线程组成的,一个进程至少有一个线程
多线程的使用:启动一个线程就是把一个函数传入并创建 Thread 实例,然后调用 start() 开始执行
1.
1 import time 2 import threading 3 4 def download(i): 5 print('开始现在文件%d'%i) 6 time.sleep(i*2) 7 print('文件%d下载完成'%i) 8 9 10 if __name__=="__main__": 11 # 单线程执行 12 # for i in range(5): 13 # download(i) 14 15 # 多线程 16 for i in range(4): 17 t=threading.Thread(target=download,args=(i,)) 18 # 启动线程 19 t.start() 20 21 while len(threading.enumerate()) > 1 : #因为进程一但启动最少有一个主线程,所以总线程最少是1 22 length=len(threading.enumerate())#threading.enumerate()当前运行的线程总数 23 #每秒检测一下系统运程的总线程数 24 print(length) 25 time.sleep(1) 26 ''' 27 输出结果: 28 开始现在文件0 29 文件0下载完成 30 开始现在文件1 31 开始现在文件2 32 开始现在文件34 33 34 4 35 文件1下载完成 36 3 37 3 38 文件2下载完成 39 2 40 2 41 文件3下载完成 42 '''
2.
1 # 使用继承自定义线程 2 import threading 3 import time 4 5 #创建一个类,继承了系统的线程类 6 class MyThread(threading.Thread): 7 #重载系统Thread类的__init__方法并加入参数num 8 def __init__(self,num): 9 super().__init__() 10 self.num=num 11 #系统Thread类的run方法是在start()后自动运行 12 def run(self): 13 time.sleep(2) 14 print('%d线程启动了'%self.num) 15 16 17 if __name__=="__main__": 18 # 实例化自定义类 19 t=MyThread(1) 20 # 启动线程对象 21 t.start() 22 time.sleep(1) 23 print('这是主线程')
3.多线程同时并发的实例展现
1 import threading,time,random 2 #在多线程类里面,每个线程都是同时运行的 3 class MyThread(threading.Thread):#继承父类的类 4 def __init__(self,num): 5 #重载父类的__init__方法 6 threading.Thread.__init__(self) 7 #将 num 参数传入父类,num参数将是到时循环 i 的值 8 self.num=num 9 def run(self):#run方法在线程start()开启时自动运行 10 #获取一个随机时间 11 tim = random.random() 12 #延迟载入随机时间 13 time.sleep(tim) 14 #输出子线程的内容和子线程运行的时间 15 print('输出的随机时间是:' + str(tim)) 16 print('我是的mnae是%s,我的num参数是%s'%(self.name,self.num)) 17 if __name__=="__main__": 18 #循环一个5次的数,将此循环5次的都利用多线程同时输出 19 for i in range(5): 20 #实例化多线程类,将每个循环赋给多线程运行 21 #重点:此多线程同时运行,但因延时的时同不相同,延迟久的将慢输出 22 t=MyThread(i) 23 #开始多线程运行 24 t.start() 25 ''' 26 输出结果:注:由于是同时并发,所在随机时间是从小到大 27 输出的随机时间是:0.07945692195157339 28 我是的mnae是Thread-1,我的num参数是0 注:num就是i的值 29 输出的随机时间是:0.23055532509566168 30 我是的mnae是Thread-4,我的num参数是3 注:此时num的值应该是2,但由于多线程是同时并发,此时 31 此第3个循环的延迟时间比第2个循环的延迟快,所以在此出现 32 输出的随机时间是:0.24012970090038033 33 我是的mnae是Thread-5,我的num参数是4 34 输出的随机时间是:0.5786985908120015 35 我是的mnae是Thread-3,我的num参数是2 36 输出的随机时间是:0.6175918079426753 37 我是的mnae是Thread-2,我的num参数是1 38 '''
4.关于子线程运程的结果响影主进程的变量的展现
1 import threading 2 g_num=1 3 4 def action1(): 5 #global 全局变量声明 6 global g_num 7 g_num+=1 8 print('g_num变量在子线程的值=%d\n'%g_num) 9 10 if __name__=="__main__": 11 #将函数引入线程运行,它的结果还是会影响到主进程 12 t1=threading.Thread(target=action1) 13 t1.start() 14 print('g_num变量在主进程的值=%d'%g_num)
5.多线程的参数传入和并发的结果展现
1 import threading,time 2 3 def work1(msg): 4 print('这是work1线程,参数为%s'%msg) 5 time.sleep(2) 6 msg.append(44) 7 print('这是work1线程,改变后的参数为%s' % msg) 8 9 def work2(msg): 10 print('这是work2线程,参数为%s'%msg) 11 12 if __name__=="__main__": 13 list1=[11,22,33] 14 t1=threading.Thread(target=work1,args=(list1,)) 15 t1.start() 16 #输出结果:这是work1线程,参数为[11, 22, 33] 17 #输出结果:这是work1线程, 改变后的参数为[11, 22, 33, 44] 18 t2=threading.Thread(target=work2,args=(list1,)) 19 t2.start() 20 # 输出结果:这是work2线程,参数为[11, 22, 33] 21 22 #线程1,线程2,运行完成后才继续执下主进程下面的程序 23 t1.join() 24 t2.join() 25 26 #主进程输出列表的值 27 print(list1) 28 # 输出结果:[11, 22, 33, 44] 29 30 ''' 31 输出结果: 32 这是work1线程,参数为[11, 22, 33] 33 这是work2线程,参数为[11, 22, 33] 34 这是work1线程,改变后的参数为[11, 22, 33, 44] 注:这个输出本来是在第二项,但因延时,所以比线程2慢输出 35 [11, 22, 33, 44] 36 '''
6.协同线程同步
1.多线程遇到的数据混乱问题
1 import threading,time 2 num=0 3 4 def work1(): 5 global num 6 for i in range(10000000): 7 num+=1 8 print('----work1---num=%d' % num) 9 def work2(): 10 global num 11 for i in range(10000000): 12 num+=1 13 print('----work2---num=%d\n'%num) 14 if __name__=="__main__": 15 16 t1=threading.Thread(target=work1) 17 t2=threading.Thread(target=work2) 18 t1.start() 19 t1.join() 20 t2.start() 21 t2.join() 22 # time.sleep(5) 23 print(num) 24 ''' 25 输出结果:下面结果,进程中所有线程是同时进行,但函数1,和函数2在计算累加过程需要时间,而主进程不会等待 26 在主进程的 print(num)的需出结果是在函数1和函数2运算中间就得出结果 172483 ,需且这个数是不确 27 定的,由于全局变量 num 的不确定性导致函数1和函数2的累加结果也成为不确定数 28 172483 29 ----work1---num=11693771 30 ----work2---num=11722647 31 ''' 32 ''' 33 输出结果:加入t1.join()和t2.join()后,结果准确了,但这样线程不是同时进行,而是串行,和不运行线程的程序无异 34 ----work1---num=10000000 35 ----work2---num=20000000 36 20000000 37 '''
2.解决问题的思路
对于本小节提出的计算错误的问题,可以通过线程同步来进行解决,思路:如下:系统调用1,然后获取到 num 的值为0 ,此时上一把锁,即不允许其他线程操作 num ,对 num 的值进
行 +1 解锁,此时 num 的值为1,其他的线程可以使用 num了,而且是 num 的值不是 0 而是 1 ,则理其他线程在对 num 进行修改时,都要先上锁,处理完后解锁,在上锁的整个过程中
不允许其他线程访问,就保证了数据的正确性
threading 模块中定义了 Lock 类,可以方便处理锁定:
创建锁:nutex = threading.Lock()
上锁: mutex.acquire()
开锁: mutex.release()
1 import threading,time 2 num=0 3 4 def work1(): 5 global num 6 #--------1.定义一个锁,这个锁的初始状态为 True---------------------------------------- 7 t1_F=mutex.acquire(True) # 返回一个状态值 8 for i in range(10000000): 9 # --------2上锁: mutex.acquire() 10 if t1_F: 11 num+=1 12 print('----work1---num=%d' % num) 13 # --------3开锁: nutex.release() 14 mutex.release() # 解锁 15 16 def work2(): 17 global num 18 # --------1.定义一个锁,这个锁的初始状态为 True---------------------------------------- 19 t2_F = mutex.acquire(True) 20 for i in range(10000000): 21 # --------2上锁: mutex.acquire() 22 if t2_F: 23 num+=1 24 print('----work2---num=%d\n'%num) 25 # --------3开锁: nutex.release() 26 mutex.release() #解锁 27 28 if __name__=="__main__": 29 # 创建锁:mutex = threading.Lock() 30 mutex=threading.Lock() 31 t1=threading.Thread(target=work1) 32 t2=threading.Thread(target=work2) 33 t1.start() 34 #t1.join() 35 t2.start() 36 #t2.join() 37 # time.sleep(5) 38 print(num) 39 ''' 40 输出结果:注:这样又能同步进行,函数时的结果就不会混乱,主进程在两个函数累加的过程中得到结果不会影响到 41 函数同部的结果 42 215481 43 ----work1---num=10000000 44 ----work2---num=20000000 45 '''