python--多线程:锁 、全局锁、Queue队列以及线程池
关于如何加锁,获取钥匙,释放锁:
- lock = threading.Lock():生成锁对象,全局唯一;
- lock.acquire():获取锁。未获取到会阻塞程序,直到获取到锁才会往下执行;
- lock.release():释放锁,归回后,其他人也可以调用;
【注意事项】:lock.acquire() 和 lock.release()必须成对出现,否则就有可能造成死锁。
为了规避这个问题,可以使用使用上下文管理器来加锁。如下所示:
import threading lock = threading.Lock() with lock: # 这里写想要实现的代码 pass
关于GIL(全局锁):
任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
【重点】python解释器中任意时刻都只有一个线程在执行;
关于Queue队列
谈及多线程,就不得不说Queue队列,这是从一个线程向另一个线程发送数据最安全的方式。创建一个被多个线程共享的 Queue 对象,这些线程通过使用put() 和 get() 操作来向队列中添加或者删除元素。
from queue import Queue # maxsize默认为0,不受限 # 一旦>0,而消息数又达到限制,q.put()也将阻塞 q = Queue(maxsize=0) # 阻塞程序,等待队列消息。 q.get() # 获取消息,设置超时时间 q.get(timeout=5.0) # 发送消息 q.put() # 等待所有的消息都被消费完 q.join() # 以下三个方法,知道就好,代码中不要使用 # 查询当前队列的消息个数 q.qsize() # 队列消息是否都被消费完,True/False q.empty() # 检测队列里消息是否已满 q.full()
关于线程池:
在Python3中,创建线程池是通过concurrent.futures函数库中的ThreadPoolExecutor类来实现的。
import Queue import threading import time ''' 这个简单的例子的想法是通过: 1、利用Queue特性,在Queue里创建多个线程对象 2、那我执行代码的时候,去queue里去拿线程! 如果线程池里有可用的,直接拿。 如果线程池里没有可用,那就等。 3、线程执行完毕,归还给线程池 ''' class ThreadPool(object): #创建线程池类 def __init__(self,max_thread=20):#构造方法,设置最大的线程数为20 self.queue = Queue.Queue(max_thread) #创建一个队列 for i in xrange(max_thread):#循环把线程对象加入到队列中 self.queue.put(threading.Thread) #把线程的类名放进去,执行完这个Queue def get_thread(self):#定义方法从队列里获取线程 return self.queue.get() def add_thread(self):#定义方法在队列里添加线程 self.queue.put(threading.Thread) pool = ThreadPool(10) def func(arg,p): print arg time.sleep(2) p.add_thread() #当前线程执行完了,我在队列里加一个线程! for i in xrange(300): thread = pool.get_thread() #线程池10个线程,每一次循环拿走一个!默认queue.get(),如果队列里没有数据就会等待。 t = thread(target=func,args=(i,pool)) t.start()
关于回调函数add_done_callback
回调函数是在调用线程完成后再调用的,在同一个线程中。
import requests import os import random import concurrent.futures as futures def download_img(url): resp = requests.get(url) filename = os.path.split(url)[1] # 获取文件名 with open(filename,'wb+') as f: f.write(resp.content) num = random.randint(2,5) print(filename + "generate:",num) time.sleep(num) return filename urls = ["http://pic27.nipic.com/20130320/8952533_092547846000_2.jpg", "http://pic19.nipic.com/20120212/9337475_104548381000_2.jpg",] ex = futures.ThreadPoolExecutor(max_workers = 3) res_iter = ex.map(download_img,urls) type(res_iter) for res in res_iter: print(res) def cf(rs): print(rs.result()) # [ex.submit(download_img,url).add_done_callback(cf) for url in urls] # for future in futures.as_completed(fu_tasks): for url in urls: f = ex.submit(download_img,url) f.add_done_callback(cf)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现