python 实用编程技巧 —— 多线程并发相关问题与解决技巧
如何使用多线程
创建一个线程类来封装数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from threading import Thread from time import sleep def handle(sid): print( 'Download...(%d)' % sid) sleep(2) print( 'Convert to...(%d)' % sid) class MyThread(Thread): # 自定义线程类 def __init__(self, sid): # Thread.__init__(self) super(MyThread, self).__init__() # 必须调用 父类的构造器 这是py2的语法,py3是super().__init__() self.sid = sid # 使用类,能够更好的封装数据 def run(self): # 新建程序的入口点,和target类似 handle(self.sid) # 更常见的做法是将handle也做为这个类的方法 if __name__ == '__main__' : t = MyThread(1) t.start() t.join() print( 'main thread' ) |
如何实现线程间的通信
GIL
- 在每个进程中, 存在一把GIL, 该进程中的线程间共享GIL
- 多线程进行时, 只有有GIL的那个线程能运行
- 通过线程间快速 传递GIL, 达到表象上的多线程, 其实同一时间只有一个线程在工作
解决方案:
- 使用标准库中的 queue.Queue, 它是一个线程安全的队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | from threading import Thread from queue import Queue from time import sleep class DownThread(Thread): def __init__(self, sid, queue): # Thread.__init__(self) super(DownThread, self).__init__() self.sid = sid self.queue = queue def downLoad(self, sid): print( "Download (%d)..." % sid) sleep(2) def run(self): self.downLoad(self.sid) data = self.sid + 100 self.queue.put((self.sid, data)) class ConvelThread(Thread): def __init__(self, queue): # Thread.__init__(self) super(ConvelThread, self).__init__() self.queue = queue def convel(self, id, data): print( "Convel (%d)-(%d)" % (id, data)) def run(self): while (True): id , data = self.queue.get() # 元组解包的形式得到数据 if (data): self.convel( id , data) if __name__ == '__main__' : q = Queue() dThreads = [DownThread(i, q) for i in range (1, 11)] cThread = ConvelThread(q) for t in dThreads: t.start() cThread.start() for t in dThreads: t.join() q.put((-1, None)) # 往队列中写入-1使 转换线程结束 cThread.join() print( 'MainThread' ) |
tar包打包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import tarfile import os def FunTarFile(tfname): tf = tarfile.open(tfname, 'w:gz' ) #open打开一个tar包,‘w’打开模式为写 ‘:gz’压缩模式gzip for fname in os.listdir( '.' ): #遍历当前目录的文件 if fname.endswith( '.docx' ): tf.add(fname) #将此文件加入tar包 os.remove(fname) #移除此文件 tf.close() print (tf.members) #打印tar包成员信息 if not tf.members: #判断tar包是否为空 os.remove(tfname) #如果tar包为空,删除此tar包 FunTarFile( 'DocxTar.tgz' ) |
事件通知使用方法
线程间的事件通知, 可以使用标准库中 Threading.Event
- 等待事件一端调用wait, 等待事件
- 通知事件一端调用 set, 通知事件
1 2 3 4 5 6 7 8 9 10 | from threading import Event,Thread def f(e): print( 'f 0' ) e.wait() #事件等待,阻塞 print( 'f 1' ) if __name__ == '__main__' : e = Event() t = Thread(target=f, args=(e,)) #创建子线程,运行f函数 t.start() # 子线程运行 e.set() # 主线程里 事件发送 |
分类:
python 实用编程技巧
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理