日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
用一個(gè)開源工具實(shí)現(xiàn)多線程Python程序的可視化

VizTracer 可以跟蹤并發(fā)的 Python 程序,以幫助記錄、調(diào)試和剖析。

并發(fā)是現(xiàn)代編程中必不可少的一部分,因?yàn)槲覀冇卸鄠€(gè)核心,有許多需要協(xié)作的任務(wù)。然而,當(dāng)并發(fā)程序不按順序運(yùn)行時(shí),就很難理解它們。對(duì)于工程師來說,在這些程序中發(fā)現(xiàn) bug 和性能問題不像在單線程、單任務(wù)程序中那么容易。

在 Python 中,你有多種并發(fā)的選擇。最常見的可能是用 threading 模塊的多線程,用subprocess 和 multiprocessing 模塊的多進(jìn)程,以及最近用 asyncio 模塊提供的 async 語法。在 VizTracer 之前,缺乏分析使用了這些技術(shù)程序的工具。

VizTracer 是一個(gè)追蹤和可視化 Python 程序的工具,對(duì)日志、調(diào)試和剖析很有幫助。盡管它對(duì)單線程、單任務(wù)程序很好用,但它在并發(fā)程序中的實(shí)用性是它的獨(dú)特之處。

嘗試一個(gè)簡單的任務(wù)

從一個(gè)簡單的練習(xí)任務(wù)開始:計(jì)算出一個(gè)數(shù)組中的整數(shù)是否是質(zhì)數(shù)并返回一個(gè)布爾數(shù)組。下面是一個(gè)簡單的解決方案:

  
 
 
 
  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 以單線程方式正常運(yùn)行它:

  
 
 
 
  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

調(diào)用堆棧報(bào)告顯示,耗時(shí)約 140ms,大部分時(shí)間花在 get_prime_arr 上。

call-stack report

這只是在數(shù)組中的元素上一遍又一遍地執(zhí)行 is_prime 函數(shù)。

這是你所期望的,而且它并不有趣(如果你了解 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()

為了配合單線程程序的工作負(fù)載,這就為三個(gè)線程使用了一個(gè) 2000 元素的數(shù)組,模擬了三個(gè)線程共享任務(wù)的情況。

Multi-thread program

如果你熟悉 Python 的全局解釋器鎖(GIL),就會(huì)想到,它不會(huì)再快了。由于開銷太大,花了 140ms 多一點(diǎn)的時(shí)間。不過,你可以觀察到多線程的并發(fā)性:

Concurrency of multiple threads

當(dāng)一個(gè)線程在工作(執(zhí)行多個(gè) is_prime 函數(shù))時(shí),另一個(gè)線程被凍結(jié)了(一個(gè) is_prime 函數(shù));后來,它們進(jìn)行了切換。這是由于 GIL 的原因,這也是 Python 沒有真正的多線程的原因。它可以實(shí)現(xiàn)并發(fā),但不能實(shí)現(xiàn)并行。

用多進(jìn)程試試

要想實(shí)現(xiàn)并行,辦法就是 multiprocessing 庫。下面是另一個(gè)使用 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 運(yùn)行它,你需要一個(gè)額外的參數(shù):

  
 
 
 
  1. viztracer --log_multiprocess my_program.py

Running with extra argument

整個(gè)程序在 50ms 多一點(diǎn)的時(shí)間內(nèi)完成,實(shí)際任務(wù)在 50ms 之前完成。程序的速度大概提高了三倍。

為了和多線程版本進(jìn)行比較,這里是多進(jìn)程版本:

Multi-process version

在沒有 GIL 的情況下,多個(gè)進(jìn)程可以實(shí)現(xiàn)并行,也就是多個(gè) is_prime 函數(shù)可以并行執(zhí)行。

不過,Python 的多線程也不是一無是處。例如,對(duì)于計(jì)算密集型和 I/O 密集型程序,你可以用睡眠來偽造一個(gè) I/O 綁定的任務(wù):

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

在單線程、單任務(wù)程序中試試:

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

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

整個(gè)程序用了 30ms 左右,沒什么特別的。

現(xiàn)在使用多線程:

  
 
 
 
  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

程序耗時(shí) 10ms,很明顯三個(gè)線程是并發(fā)工作的,這提高了整體性能。

用 asyncio 試試

Python 正在嘗試引入另一個(gè)有趣的功能,叫做異步編程。你可以制作一個(gè)異步版的任務(wù):

  
 
 
 
  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 從字面上看是一個(gè)帶有任務(wù)的單線程調(diào)度器,你可以直接在它上使用 VizTracer:

VizTracer with asyncio

依然花了 10ms,但顯示的大部分函數(shù)都是底層結(jié)構(gòu),這可能不是用戶感興趣的。為了解決這個(gè)問題,可以使用 --log_async 來分離真正的任務(wù):

  
 
 
 
  1. viztracer --log_async my_program.py

Using --log_async to separate tasks

現(xiàn)在,用戶任務(wù)更加清晰了。在大部分時(shí)間里,沒有任務(wù)在運(yùn)行(因?yàn)樗ㄒ蛔龅氖虑榫褪撬X)。有趣的部分是這里:

Graph of task creation and execution

這顯示了任務(wù)的創(chuàng)建和執(zhí)行時(shí)間。Task-1 是 main() 協(xié)程,創(chuàng)建了其他任務(wù)。Task-2、Task-3、Task-4 執(zhí)行 io_task 和 sleep 然后等待喚醒。如圖所示,因?yàn)槭菃尉€程程序,所以任務(wù)之間沒有重疊,VizTracer 這樣可視化是為了讓它更容易理解。

為了讓它更有趣,可以在任務(wù)中添加一個(gè) time.sleep 的調(diào)用來阻止異步循環(huán):

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

time.sleep call

程序耗時(shí)更長(40ms),任務(wù)填補(bǔ)了異步調(diào)度器中的空白。

這個(gè)功能對(duì)于診斷異步程序的行為和性能問題非常有幫助。

看看 VizTracer 發(fā)生了什么?

通過 VizTracer,你可以在時(shí)間軸上查看程序的進(jìn)展情況,而不是從復(fù)雜的日志中想象。這有助于你更好地理解你的并發(fā)程序。

VizTracer 是開源的,在 Apache 2.0 許可證下發(fā)布,支持所有常見的操作系統(tǒng)(Linux、macOS 和 Windows)。你可以在 VizTracer 的 GitHub 倉庫中了解更多關(guān)于它的功能和訪問它的源代碼。


網(wǎng)頁題目:用一個(gè)開源工具實(shí)現(xiàn)多線程Python程序的可視化
文章URL:http://m.5511xx.com/article/coeggcg.html