一 . current_thread的用法
import threading import time from threading import Thread, current_thread def func(n): time.sleep(1) print('子线程名称', current_thread().getName()) # Thread-1 print(f'{n}号线程任务') if __name__ == '__main__': t = Thread(target=func, args=(1,)) t.start() print('主线程名称',current_thread().getName()) # MainThread print('主线程ID',current_thread().ident) print(current_thread()) # 当前运行的进程 print(threading.enumerate()) # 列举正在运行的线程 print(threading.active_count()) # 查看有多少正在运行的线程
二 . 线程队列(重点)
1. 先进先出(FIFO)队列 (常用)
import queue # 一:先进先出队列 q = queue.Queue(3) #先进先出 fifo first in first out q.put(1) q.put(2) print('当前队列内容长度',q.qsize()) q.put(3) print('查看队列是否满了', q.full()) try: q.put_nowait(4) # 用put_nowait 因为队列是共享的,不确定谁往里面放东西,所以用它试错 except Exception: print('队列满了') print(q.get()) print(q.get()) print('查看队列是否为空', q.empty()) print(q.get()) print('查看队列是否为空', q.empty()) try: q.get_nowait() # queue.Empty except Exception: print('队列空了')
2.先进后出(FILO) (常用)
import queue # 二 先进后出队列,或者后进先出,类似于栈 q = queue.LifoQueue(3) q.put('乔峰') q.put('段誉') q.put('虚竹') print(q.get()) # 虚竹 print(q.get()) # 段誉 print(q.get()) # 乔峰
3.优先级队列 (不常用)
import queue #优先级队列 q = queue.PriorityQueue(5) # 先比较元组前边数字的大小,数字越小优先级越高, -1 < 0 < 1, # 如果数字相同,比较元元组第二项 q.put((5,'alex')) q.put((2,'宝宝')) q.put((7,'大力')) print(q.get()) # (2, '宝宝') print(q.get()) # (5, 'alex') print(q.get()) # (7, '大力')
三 . 线程池(重点)
import time from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor def f1(n,s): time.sleep(1) # print(n,s) return f'{n}号' + s if __name__ == '__main__': tp = ThreadPoolExecutor(4) # 线程的个数里面的参数 * 5, 不写参数就是cpu核数 * 5 # tp = ProcessPoolExecutor(4) 这个是进程,这个方法比较常用 进程的个数就是参数,不写就是cpu 的核数 # tp.map(f1,range(10)) #异步提交任务(瞬间提交,不执行里面的函数),参数同样是任务名称,可迭代对象 res_list = [] for i in range(10): # tp.submit(f1,{'段誉':i},'六脉神剑') 不能接返回值,下面的可以 res = tp.submit(f1,i,'乔峰') # submit是给线程池异步提交任务, 里面随便传参 print(res) res_list.append(res) tp.shutdown() #主线程等待所有提交给线程池的任务,全部执行完毕 close + join for r in res_list: print(r.result()) # 相当于 进程里面的 .get() print('主线程结束')
四. 协程
1. 生成器版协程(最low,了解)
import time def f1(): for i in range(5): time.sleep(0.5) print('f1>>',i) yield def f2(): g = f1() for i in range(5): time.sleep(0.5) print('f2>>', i) next(g) f1() f2() # f2 与 f1 交替出值
2. greenlet版协程(中档,了解)
import time from greenlet import greenlet def f1(n): print('第一次执行f1' + n) time.sleep(1) g2.switch('阿朱') # 第一次传参就行 以后的g2.switch() 不用传参 print('第二次执行f1' + n) g2.switch() def f2(n): print('第一次执行f2' + n) time.sleep(1) g1.switch() print('第二次执行f2' + n) g1 = greenlet(f1) # 实例化一个greenlet对象,并将任务名称作为参数参进去 g2 = greenlet(f2) g1.switch('乔峰') # 里面可以传参, 执行g1里面的任务
3. gevent 真正的协程(重点)
from gevent import monkey;monkey.patch_all() import gevent import time def f1(): print('第一次f1') # gevent.sleep(1) time.sleep(2) print('第二次f1') return 15 def f2(): print('第一次f2') # gevent.sleep(2) time.sleep(3) print('第二次f2') s = time.time() g1 = gevent.spawn(f1) # 异步提交了f1任务 g2 = gevent.spawn(f2) # 异步提交了f2任务 gevent.joinall([g1,g2]) # 必须joinall 不然主协程代码执行结束后就结束,不管上面的代码是否执行 e = time.time() print('执行时间:', e-s) print('主程序任务') # 两个gevent模块必须都导入,如果只import gevent模块,那么只有gevent.sleep()这种IO模式可以并发,其他IO不支持并发
上例gevent.sleep(2)模拟的是gevent可以识别的 IO阻塞. 而time.sleep(2)或者是其他的阻塞, gevent是不能直接识别的, 需要用下面的一段代码, 打补丁, 就可以识别了. from gevent import monkey;monkey.patch_all, 这段代码必须放在被打补丁者的前面,如time, socket模块之前. (只要用到gevent 就直接写在最前面把)
五 . 线程池回调函数
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor def f1(n,s): return n+s def f2(n): print('回调函数>>>', n.result()) # 回调函数>>> 23 if __name__ == '__main__': tp = ThreadPoolExecutor(4) res = tp.submit(f1, 11, 12).add_done_callback(f2)