37.线程池 ,线性队列,时间,线程池
今日内容大纲
昨日内容回顾 线程:两种创建方式 线程与进程的效率对比 同一进程下线程是共享进程资源的 锁(同步锁/互斥锁 Lock) 死锁现象 : 当我们使用锁嵌套锁的时候多个线程异步执行的时候会出现线程之前互相等待 对方未释放的锁 递归锁 Rlock 守护线程 : 主进程代码结束,守护进程结束 信号量 大保健 今日内容 线程的一些其他方法 线程事件 线程队列 (重点) 线程池 重点
线程的一些其他方法
Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 # threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。 # 正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量, # 与len(threading.enumerate())有相同的结果
from threading import Thread # import threading # import time # from multiprocessing import Process # import os # # # def work(): # time.sleep(1) # print('子线程》》》',threading.get_ident()) # print(threading.current_thread().getName()) # print(threading.current_thread()) # # if __name__ == '__main__': # #在主进程下开启线程 # t = Thread(target=work) # t.start() # # print('主线程对象',threading.current_thread())#主线程对象 # print('主线程名称',threading.current_thread().getName())#主线程名称 # print('主线程id',threading.get_ident()) #主线程id # print('主线程id另一种方法',threading.current_thread().ident) #主线程ID # # # time.sleep(3) # print(threading.enumerate()) # print(threading.active_count()) from threading import Thread import threading import time def sayhi(name): time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t=Thread(target=sayhi,args=('太白',)) t2=Thread(target=sayhi,args=('alex',)) t.start() t2.start() t.join() #因为这个线程用了join方法,主线程等待子线程的运行结束 print('主线程') print(t.is_alive()) #所以t这个线程肯定是执行结束了,结果为False print(t2.is_alive()) #有可能是True,有可能是False,看子线程和主线程谁执行的快
事件
线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作
,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,
它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,
那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。
如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行 事件的基本方法: event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; event.clear():恢复event的状态值为False。
# from threading import Thread,Event # #e的状态会有两种,False,True 当事件状态为False 的时候,wait 的地方会阻塞 # e = Event() # e.set() #将事件对象的状态改为True # # e.clear() #将事件对象的状态改为False # print('在这里等待') # print(e.is_set())# 判断Event事件的状态 # e.wait()#如果事件等于False 将阻塞 # print('还没好') '''有多个工作线程尝试链接MySQL,我们想要在链接前确保MySQL服务正常才让那些工作 线程去连接MySQL服务器,如果连接不成功,都会去尝试重新连接。 那么我们就可以采用threading.Event机制来协调各个工作线程的连接操作''' from threading import Thread,Event import threading import time,random def conn_mysql(): count=1 while not event.is_set(): if count > 3: raise TimeoutError('链接超时') #自己发起错误 print('<%s>第%s次尝试链接' % (threading.current_thread().getName(), count)) event.wait(0.5) # count+=1 print('<%s>链接成功' %threading.current_thread().getName()) def check_mysql(): print('\033[45m[%s]正在检查mysql\033[0m' % threading.current_thread().getName()) t1 = random.randint(0,3) print('>>>>',t1) time.sleep(t1) event.set() if __name__ == '__main__': event=Event() check = Thread(target=check_mysql) conn1=Thread(target=conn_mysql) conn2=Thread(target=conn_mysql) check.start() conn1.start() conn2.start()
队列(重点)
# import queue # #先进先出FIFO # # q = queue.Queue(3) # q.put('first') # q.put('second') # q.put('third') # try: # q.put_nowait('DDHJD') # except : # print('满了,还他妈塞') # print(q.get()) # print(q.get()) # print(q.get()) # try : # q.get_nowait() # except: # print('能吃多少自己心里没点逼数啊') #后进先出,先进后出 # import queue # q = queue.LifoQueue(3)#队列,类似于栈,先进后出的顺序 # q.put(1) # q.put(2) # q.put(3) # try: # q.put_nowait(4) # except: # print('人家第一次嘛,你塞这么多干嘛呀') # print(q.get()) # print(q.get()) # print(q.get()) # try: # q.get_nowait(4) # except: # print('死鬼,吃不够') #优先级队列 import queue q = queue.PriorityQueue(3) #put 进入一个元祖,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较, # 数字越小,优先级越高) # q.put((-10,'a')) # q.put((-5,'b'))#负数也可以 # q.put((-10,'c')) # print(q.get()) # print(q.get()) # print(q.get()) #如果两个值得优先级一样,那么按照后面的ASCII码顺序来排序,如果字符串 # 第一个元素相同,比较第二个元素的ASCII码顺序 q.put((20,'ws')) q.put((20,{'a':11})) print(q.get()) print(q.get()) #优先级相同的两个数据,他们后面的值必须是相同的数据类型才能比较,可 # 以是元组,也是通过元素的ASCII码来排序 #结果 (数字越小,优先级越高,优先级越高,越先出队)
线程池
import time import os import threading from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor def func(n): time.sleep(2) print('%s打印的:'%(threading.get_ident()),n) return n*n tpool = ThreadPoolExecutor(max_workers=5) #默认一般起线程的数据不超过CPU个数*5 # tpool = ProcessPoolExecutor(max_workers=5) #进程池的使用只需要将上面的ThreadPoolExecutor改为ProcessPoolExecutor就行了,其他都不用改 #异步执行 t_lst = [] for i in range(5): t = tpool.submit(func,i) #提交执行函数,返回一个结果对象,i作为任务函数的参数 def submit(self, fn, *args, **kwargs): 可以传任意形式的参数 t_lst.append(t) # # print(t.result()) #这个返回的结果对象t,不能直接去拿结果,不然又变成串行了,可以理解为拿到一个号码,等所有线程的结果都出来之后,我们再去通过结果对象t获取结果 tpool.shutdown() #起到原来的close阻止新任务进来 + join的作用,等待所有的线程执行完毕 print('主线程') for ti in t_lst: print('>>>>',ti.result()) # import time # from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor # from threading import current_thread # # def func(n): # time.sleep(1) # print(n,current_thread().ident) # return n**2 # if __name__ == '__main__': # t_p = ThreadPoolExecutor(max_workers = 4) # map_res = t_p.map(func,range(10)) #异步执行的map自带join功能 # print(map_res) # print([i for i in map_res]) # # #将进程池和线程池放到一起呢,是为了统一使用方式,使用threadPollExecutor和ProcessPollExecutor的方式一样, # # 而且只要通过这个concurrent.futures导入就可以直接用他们两个了 ''' concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor:线程池,提供异步调用 ProcessPoolExecutor: 进程池,提供异步调用 Both implement the same interface, which is defined by the abstract Executor class. ''' #2 基本方法 #submit(fn, *args, **kwargs) # 异步提交任务 #map(func, *iterables, timeout=None, chunksize=1) # 取代for循环submit的操作 #shutdown(wait=True) '''相当于进程池的pool.close()+pool.join()操作 wait=True,等待池内所有任务执行完毕回收完资源后才继续 wait=False,立即返回,并不会等待池内的任务执行完毕 但不管wait参数为何值,整个程序都会等到所有任务执行完毕 submit和map必须在shutdown之前''' #result(timeout=None) # 取得结果 #add_done_callback(fn) # 回调函数
线程池的一些其他方法
# import time # from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor # from threading import current_thread # # def func(n): # time.sleep(1) # # print(n,current_thread().ident) # return n**2 # def func2(n): # print(n) # # if __name__ == '__main__': # # t_p = ThreadPoolExecutor(max_workers = 4)#创建线程池的线程数量 # t_p = ProcessPoolExecutor(max_workers = 4)#创建进程池的线程数量 # # # t_p.submit(func,n = 1) # t_res_list = [] # for i in range(10): # res_obj = t_p.submit(func,i) #异步提交了这十个任务 # t_res_list.append(res_obj) # t_p.shutdown() #close+join锁定线程池,等待线程池所有结果执行完以后 # # 再打印结果 任务执行完以后结果对象里面有结果 # # print(t_res_list) #打印的类似于生成器的所有的结果都卡在yield处 # for e_res in t_res_list: # print(e_res.result()) # #
回调函数
import time from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor from threading import current_thread def func(n): time.sleep(1) # print(n,current_thread().getName()) #执行的线程名字 return n**2 def func2(n): # print('>>>>>>>>>',n) # print('>>>>>>>>>>>',n.result()) print('current_thread>>>',current_thread().getName()) if __name__ == '__main__': t_p = ThreadPoolExecutor(max_workers = 4) # t_p = ProcessPoolExecutor(max_workers = 4) for i in range(3): t_p.submit(func,i).add_done_callback(func2) print('主线程结束')
多线程和多进程进行纯运算的效率
import time from multiprocessing import Process from threading import Thread def func(): num = 0 for i in range(1,100000000): num += i # def func(): # time.sleep(2) # print('xxxxxxx') if __name__ == '__main__': p_s_t = time.time() p_list = [] for i in range(10): p = Process(target= func,) p_list.append(p) p.start() [pp.join() for pp in p_list] p_e_t = time.time() p_dif_t = p_e_t - p_s_t t_s_t = time.time() t_list = [] for i in range(10): t = Thread(target = func,) t_list.append(t) t.start() [tt.join() for tt in t_list] t_e_t = time.time() t_dif_t = t_e_t - t_s_t print('多线程的执行时间',t_dif_t) print('多进程的执行时间',p_dif_t)
greenlet模块的使用
import time from greenlet import greenlet def eat(name): print('%s eat 1'%name) time.sleep(3) g2.switch('taibai') print('%s eat 2'%name) g2.switch() def play(name): print('%s play 1'%name) time.sleep(3) g1.switch() print('%s play 2'%name) g1 = greenlet(eat) g2 = greenlet(play) g1.switch('taibai')
协程
''' 协程:单线程下实现并发 并发:伪并行,遇到IO就切换,单核下多个任务之间切换进行,效果像是几个程序同时运行,提高效率 任务切换+保存状态 并行:多核CPU,真正的同时执行 串行:一个任务执行完再执行另一个任务 多线程多进程下的任务切换+保存状态是操作系统进行的 ''' # 串行 # import time # def func1(): # time.sleep(1) # print('func1') # def func2(): # time.sleep(1) # print('func2') # if __name__ == '__main__': # func1() # func2() #基于yield并发执行,多任务之间来回切换,这就是个简单的协程提现, # 但是能够节省IO时间吗,不能 import time def consumer(): '''任务一,接收数据,处理数据''' while True: x = yield time.sleep(1)#只是进行了切换,但并没有节省IO时间 print('处理了数据:',x) def producer(): '''任务二,生产数据''' g = consumer() next(g) #找到了consumer函数的yield位置 for i in range(3): g.send(i) #给yield传值,然后循环给下一个yield传值,并且多了切换的 #程序,比直接串行执行还多了一些步骤,导致执行效率反而更低了 print('发送了数据',i) start = time.time() ''' 基于yield保存状态,实现两个任务直接来回切换,即并发效果 如果两个任务都加上打印,那么明显的看到两个任务的打印是你一次我一次,并发执行 ''' # producer() #在当前线程中执行了这个函数 # #但是通过这个函数里面的send切换到了另外一个任务 # stop = time.time() # 串行执行的方式 res = producer() consumer(res) stop = time.time() print(stop - start)
GIL锁

'''python最早期的时候对于多线程也加锁,但是python比较极端的(在当时电脑cpu确 实只有1核)加了一个GIL全局解释锁,是解释器级别的,锁的是整个线程,而不是线程 里面的某些数据操作,每次只能有一个线程使用cpu,也就说多线程用不了多核,但是他 不是python语言的问题,是CPython解释器的特性,如果用Jpython解释器是没有这 个问题的,Cpython是默认的,因为速度快,Jpython是java开发的,在Cpython里 面就是没办法用多核,这是python的弊病,历史问题,'''
# import time # def consumer(): # for i in range(10): # x = yield # time.sleep(1) # print('处理了数据',i) # def producer(): # g =consumer() # next(g) # for i in range(10): # g.send(i) # print('发送了数据',i) # start = time.time() # producer() # stop = time.time() # print(stop - start) # import time # def consumer(): # for i in range(4): # time.sleep(1) # print('处理了数据',i) # def producer(): # for i in range(3): # print('发送了数据',i) # start = time.time() # consumer() # # producer() # # stop = time.time() # print('>>>>>>>>>',stop - start) import time def consumer(): for i in range(4): x = yield time.sleep(1) print('处理了数据',i) def producer(): g = consumer() next(g) for i in range(3): g.send(i) print('发送了数据',i) start = time.time() producer() end = time.time() print(end - start)
浙公网安备 33010602011771号