当前位置: 首页 > news >正文

Python 高手编程系列三千四百零一:使用线程池

我们尝试解决的第一个问题是程序运行中未绑定限制的线程。一个好的解决方案是构
建一个严格定义大小的工作线程池,它将处理所有的并行工作,并通过一些线程安全的数
据结构与工作线程进行通信。通过使用这个线程池的方案,我们还会很容易地解决我们刚
才提到的另外两个问题。
所以通常的想法是启动一些预定义数量的线程,它将从队列中消费工作项,直到完成。
当没有其他工作要做时,线程将返回,我们将能够退出程序。对于我们的结构,Queue 类
是用于与工作线程通信的很好的选择,它来自内置的 queue 模块。它是一个 FIFO(先进
先出)队列实现,它非常类似于 collections 模块的 deque 集合,专门设计用于处理线
程间通信。这里是一个修改版本的 main()函数,它只启动有限数量的工作线程,并使用
一个新的 worker()函数作为目标,并使用线程安全的队列与它们通信,如下所示:
from queue import Queue, Empty
from threading import Thread
THREAD_POOL_SIZE = 4
def worker(work_queue):
while not work_queue.empty():
try:
item = work_queue.get(block=False)
except Empty:
break
else:
fetch_place(item)
work_queue.task_done()
def main():
work_queue = Queue()
for place in PLACES:
work_queue.put(place)
threads = [
Thread(target=worker, args=(work_queue,))
for _ in range(THREAD_POOL_SIZE)
]
for thread in threads:
thread.start()
work_queue.join()
while threads:
threads.pop().join()
运行这个修改版本的程序的的结果类似于前一个,如下所示:
$ python threadpool.py
Reykjavík, Iceland, 64.13, -21.82
Venice, Italy, 45.44, 12.32
Vienna, Austria, 48.21, 16.37
Zadar, Croatia, 44.12, 15.23
Wrocław, Poland, 51.11, 17.04
Bologna, Italy, 44.49, 11.34
Slubice, Poland, 52.35, 14.56
Berlin, Germany, 52.52, 13.40
New York, NY, USA, 40.71, -74.01
Dehli, Gujarat, India, 21.57, 73.22
time elapsed: 1.20s
运行时间比为每个参数启动一个线程的情况更慢,但是至少现在不可能被任意长的输
入耗尽所有计算资源。此外,我们可以调整 THREAD_POOL_SIZE 参数以获得更好的资源/
时间的平衡。
使用双向队列
我们现在能够解决的另一个问题是在线程中输出的潜在的有问题的打印。更好的方式
是启动另外的线程打印,而不是在主线程中进行。我们可以通过提供另一个队列来处理它,
这个队列将负责从我们的工作线程中收集结果。这里是完整的代码,把一切都放到一起,
对主要的变化进行突出显示,如下所示:
import time
from queue import Queue, Empty
from threading import Thread
from gmaps import Geocoding
api = Geocoding()
PLACES = (
‘Reykjavik’, ‘Vien’, ‘Zadar’, ‘Venice’,
‘Wrocław’, ‘Bolognia’, ‘Berlin’, ‘Słubice’,
‘New York’, ‘Dehli’,
)
THREAD_POOL_SIZE = 4
def fetch_place(place):
return api.geocode(place)[0]
def present_result(geocoded):
print(“{:>25s}, {:6.2f}, {:6.2f}”.format(
geocoded[‘formatted_address’],
geocoded[‘geometry’][‘location’][‘lat’],
geocoded[‘geometry’][‘location’][‘lng’],
))
def worker(work_queue, results_queue):
while not work_queue.empty():
try:
item = work_queue.get(block=False)
except Empty:
break
else:
results_queue.put(
fetch_place(item)
)
work_queue.task_done()
def main():
work_queue = Queue()
results_queue = Queue()
for place in PLACES:
work_queue.put(place)
threads = [
Thread(target=worker, args=(work_queue, results_queue))
for _ in range(THREAD_POOL_SIZE)
]
for thread in threads:
thread.start()
work_queue.join()
while threads:
threads.pop().join()
while not results_queue.empty():
present_result(results_queue.get())
ifname== “main”:
started = time.time()
main()
elapsed = time.time() - started
print()
print(“time elapsed: {:.2f}s”.format(elapsed))
这消除了格式化输出的风险,如果 present_result()函数执行更多的 print()语
句或执行一些额外的计算,我们可以体验到。在小的输入下,我们不奢望这种方法会有任
何性能改进,但实际上,我们还减少由于缓慢的 print()执行的线程串行化的风险。这是
我们的最终的输出如下:
$ python threadpool_with_results.py
Vienna, Austria, 48.21, 16.37
Reykjavík, Iceland, 64.13, -21.82
Zadar, Croatia, 44.12, 15.23
Venice, Italy, 45.44, 12.32
Wrocław, Poland, 51.11, 17.04
Bologna, Italy, 44.49, 11.34
Slubice, Poland, 52.35, 14.56
Berlin, Germany, 52.52, 13.40
New York, NY, USA, 40.71, -74.01
Dehli, Gujarat, India, 21.57, 73.22
time elapsed: 1.30s

http://www.jsqmd.com/news/996896/

相关文章:

  • Kafka 灾难回放机制:基于事件事实流的计数全量恢复方案
  • LangGraph图模型实战:构建可调试、可扩展的AI智能体
  • Tabula终极指南:3分钟快速掌握PDF表格数据提取技巧
  • 如何利用SUSI Firefox Bot提升浏览器智能助手体验?
  • Pandas生产级数据处理17条不可协商铁律
  • 2026年金属雕塑行业观察:从设计到落地,这些雕塑厂家值得关注 - 优质品牌商家
  • 文档智能处理革命:跨平台内容采集系统的技术架构与应用实践
  • 宁德时代怎么分析?4 步搞定行情、估值到买卖决策
  • 北京研学机构哪家好?求推荐靠谱的孩子独立北京行,老师负责的研学机构 - 品牌2026
  • 如何通过AI视觉重构技术从单张图片生成专业级材质贴图
  • 2026赤峰离婚律师避坑指南:5位经验丰富口碑好的靠谱推荐 - 本地品牌推荐
  • 生产级PDF文档问答系统:Python手写RAG流水线实战
  • 【Linux网络】深入理解 TCP 协议(一):报头设计与可靠性基石
  • 告别抓瞎!用C#和网络调试工具一步步拆解三菱PLC的A-1E报文(附模拟器实战)
  • Java的4类8种基本数据类型
  • OpCore-Simplify:重新定义黑苹果配置的技术哲学与实践
  • Rasa Action Server 异步调用实战:从原理到高可用落地
  • 2026年成都盘扣式钢管架租赁市场观察:本地服务商综合能力解析与案例参考 - 优质品牌商家
  • 如何用moderncv打造专业简历:LaTeX排版终极指南
  • Facebook Prophet季节性建模:从业务语义到可解释周期分解
  • 计算机毕业设计之驿途系统
  • Plotly Express实战指南:三行代码构建交互式数据看板
  • FlexCAN(FD) MB地址计算函数详解:从寄存器位域到C语言指针的跨越
  • 从“直通”到“炸管”:手把手分析一个MOS管驱动电路的失败案例
  • Rust加速Python数据科学:Polars/TikToken/River/HyperJSON实战指南
  • hot100 33.搜索旋转排序数组
  • AI推广品牌哪家好,按年收费且性价比高的有哪些 - mypinpai
  • 别再傻傻分不清了!C语言中算术移位、逻辑移位和循环移位的区别与实战避坑指南
  • 创维E900V22D刷Armbian系统终极指南:从电视盒子到高性能服务器的完美蜕变
  • 别再让需求文档睡大觉了!用Aspice SWE.1的8个实践,盘活你的软件需求分析