我的编程空间,编程开发者的网络收藏夹
学习永远不晚

用一个开源工具实现多线程 Python 程序的可视化

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

用一个开源工具实现多线程 Python 程序的可视化

VizTracer 可以跟踪并发的 Python 程序,以帮助记录、调试和剖析。

并发是现代编程中必不可少的一部分,因为我们有多个核心,有许多需要协作的任务。然而,当并发程序不按顺序运行时,就很难理解它们。对于工程师来说,在这些程序中发现 bug 和性能问题不像在单线程、单任务程序中那么容易。

在 Python 中,你有多种并发的选择。最常见的可能是用 threading 模块的多线程,用subprocess 和 multiprocessing 模块的多进程,以及最近用 asyncio 模块提供的 async 语法。在 VizTracer 之前,缺乏分析使用了这些技术程序的工具。

VizTracer 是一个追踪和可视化 Python 程序的工具,对日志、调试和剖析很有帮助。尽管它对单线程、单任务程序很好用,但它在并发程序中的实用性是它的独特之处。

尝试一个简单的任务

从一个简单的练习任务开始:计算出一个数组中的整数是否是质数并返回一个布尔数组。下面是一个简单的解决方案:

  1. def is_prime(n):
  2. for i in range(2, n):
  3. if n % i == 0:
  4. return False
  5. return True
  6.  
  7. def get_prime_arr(arr):
  8. return [is_prime(elem) for elem in arr]

试着用 VizTracer 以单线程方式正常运行它:

  1. if __name__ == "__main__":
  2. num_arr = [random.randint(100, 10000) for _ in range(6000)]
  3. get_prime_arr(num_arr)
  1. viztracer my_program.py

 

Running code in a single thread

调用堆栈报告显示,耗时约 140ms,大部分时间花在 get_prime_arr 上。

 

call-stack report

这只是在数组中的元素上一遍又一遍地执行 is_prime 函数。

这是你所期望的,而且它并不有趣(如果你了解 VizTracer 的话)。

试试多线程程序

试着用多线程程序来做:

  1. if __name__ == "__main__":
  2.     num_arr = [random.randint(100, 10000) for i in range(2000)]
  3.     thread1 = Thread(target=get_prime_arr, args=(num_arr,))
  4.     thread2 = Thread(target=get_prime_arr, args=(num_arr,))
  5.     thread3 = Thread(target=get_prime_arr, args=(num_arr,))
  6.  
  7.     thread1.start()
  8.     thread2.start()
  9.     thread3.start()
  10.  
  11.     thread1.join()
  12.     thread2.join()
  13.     thread3.join()

为了配合单线程程序的工作负载,这就为三个线程使用了一个 2000 元素的数组,模拟了三个线程共享任务的情况。

 

Multi-thread program

如果你熟悉 Python 的全局解释器锁(GIL),就会想到,它不会再快了。由于开销太大,花了 140ms 多一点的时间。不过,你可以观察到多线程的并发性:

 

Concurrency of multiple threads

当一个线程在工作(执行多个 is_prime 函数)时,另一个线程被冻结了(一个 is_prime 函数);后来,它们进行了切换。这是由于 GIL 的原因,这也是 Python 没有真正的多线程的原因。它可以实现并发,但不能实现并行。

用多进程试试

要想实现并行,办法就是 multiprocessing 库。下面是另一个使用 multiprocessing 的版本:

  1. if __name__ == "__main__":
  2.     num_arr = [random.randint(100, 10000) for _ in range(2000)]
  3.    
  4.     p1 = Process(target=get_prime_arr, args=(num_arr,))
  5.     p2 = Process(target=get_prime_arr, args=(num_arr,))
  6.     p3 = Process(target=get_prime_arr, args=(num_arr,))
  7.  
  8.     p1.start()
  9.     p2.start()
  10.     p3.start()
  11.  
  12.     p1.join()
  13.     p2.join()
  14.     p3.join()

要使用 VizTracer 运行它,你需要一个额外的参数:

  1. viztracer --log_multiprocess my_program.py

 

Running with extra argument

整个程序在 50ms 多一点的时间内完成,实际任务在 50ms 之前完成。程序的速度大概提高了三倍。

为了和多线程版本进行比较,这里是多进程版本:

 

Multi-process version

在没有 GIL 的情况下,多个进程可以实现并行,也就是多个 is_prime 函数可以并行执行。

不过,Python 的多线程也不是一无是处。例如,对于计算密集型和 I/O 密集型程序,你可以用睡眠来伪造一个 I/O 绑定的任务:

  1. def io_task():
  2.     time.sleep(0.01)

在单线程、单任务程序中试试:

  1. if __name__ == "__main__":
  2.     for _ in range(3):
  3.         io_task()

 

I/O-bound single-thread, single-task program

整个程序用了 30ms 左右,没什么特别的。

现在使用多线程:

  1. if __name__ == "__main__":
  2.     thread1 = Thread(target=io_task)
  3.     thread2 = Thread(target=io_task)
  4.     thread3 = Thread(target=io_task)
  5.  
  6.     thread1.start()
  7.     thread2.start()
  8.     thread3.start()
  9.  
  10.     thread1.join()
  11.     thread2.join()
  12.     thread3.join()

 

I/O-bound multi-thread program

程序耗时 10ms,很明显三个线程是并发工作的,这提高了整体性能。

用 asyncio 试试

Python 正在尝试引入另一个有趣的功能,叫做异步编程。你可以制作一个异步版的任务:

  1. import asyncio
  2.  
  3. async def io_task():
  4.     await asyncio.sleep(0.01)
  5.  
  6. async def main():
  7.     t1 = asyncio.create_task(io_task())
  8.     t2 = asyncio.create_task(io_task())
  9.     t3 = asyncio.create_task(io_task())
  10.  
  11.     await t1
  12.     await t2
  13.     await t3
  14.  
  15. if __name__ == "__main__":
  16.     asyncio.run(main())

由于 asyncio 从字面上看是一个带有任务的单线程调度器,你可以直接在它上使用 VizTracer:

 

VizTracer with asyncio

依然花了 10ms,但显示的大部分函数都是底层结构,这可能不是用户感兴趣的。为了解决这个问题,可以使用 --log_async 来分离真正的任务:

  1. viztracer --log_async my_program.py

 

Using --log_async to separate tasks

现在,用户任务更加清晰了。在大部分时间里,没有任务在运行(因为它唯一做的事情就是睡觉)。有趣的部分是这里:

 

Graph of task creation and execution

这显示了任务的创建和执行时间。Task-1 是 main() 协程,创建了其他任务。Task-2、Task-3、Task-4 执行 io_task 和 sleep 然后等待唤醒。如图所示,因为是单线程程序,所以任务之间没有重叠,VizTracer 这样可视化是为了让它更容易理解。

为了让它更有趣,可以在任务中添加一个 time.sleep 的调用来阻止异步循环:

  1. async def io_task():
  2.     time.sleep(0.01)
  3.     await asyncio.sleep(0.01)

 

time.sleep call

程序耗时更长(40ms),任务填补了异步调度器中的空白。

这个功能对于诊断异步程序的行为和性能问题非常有帮助。

看看 VizTracer 发生了什么?

通过 VizTracer,你可以在时间轴上查看程序的进展情况,而不是从复杂的日志中想象。这有助于你更好地理解你的并发程序。

VizTracer 是开源的,在 Apache 2.0 许可证下发布,支持所有常见的操作系统(Linux、macOS 和 Windows)。你可以在 VizTracer 的 GitHub 仓库中了解更多关于它的功能和访问它的源代码。

 

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

用一个开源工具实现多线程 Python 程序的可视化

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

用一个开源工具实现多线程 Python 程序的可视化

VizTracer 是一个追踪和可视化 Python 程序的工具,对日志、调试和剖析很有帮助。尽管它对单线程、单任务程序很好用,但它在并发程序中的实用性是它的独特之处。

怎样用一个开源工具实现多线程 Python 程序的可视化

今天就跟大家聊聊有关怎样用一个开源工具实现多线程 Python 程序的可视化,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。VizTracer 可以跟踪并发的 Python 程序,以帮
2023-06-15

实用!一款开源的 JSON 可视化管理工具

这是一款很实用的 JSON 工具,可以更轻松直观地查看 JSON 文档。

5 个流行的用于远程工作和在线会议的开源视频会议工具

你会发现网上有一些视频会议工具。一些是为专业用户量身定制的,另一些是为了日常闲聊的。

大一新生开发的小工具火了!可视化Python编程体验了解一下

普普通通黑底白字地敲代码太枯燥?那么,把Python脚本可视化怎么样?

开源 | AREX:携程新一代自动化回归测试工具的设计与实现

AREX经过不断优化,逐步达到了用真实流量和数据进行回归测试的目标,降低了成本,提高了质量,达到了建设初期设定的目标。
开源技术2024-11-30

如何使用Python中的多线程和协程实现一个高性能的爬虫

如何使用Python中的多线程和协程实现一个高性能的爬虫导语:随着互联网的快速发展,爬虫技术在数据采集和分析中扮演着重要的角色。而Python作为一门强大的脚本语言,具备多线程和协程的功能,可以帮助我们实现高性能的爬虫。本文将介绍如何使用P
2023-10-22

热门标签

编程热搜

编程资源站

目录