溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python多進程,多線程及GIL全局解釋器鎖實例分析

發布時間:2022-07-19 11:30:26 來源:億速云 閱讀:163 作者:iii 欄目:開發技術

這篇文章主要講解了“Python多進程,多線程及GIL全局解釋器鎖實例分析”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Python多進程,多線程及GIL全局解釋器鎖實例分析”吧!

    1. 并發與并行

    • 所謂的并行(Parallelism),就是多個彼此獨立的任務可以同時一起執行,彼此并不相互干擾,并行強調的是同時且獨立的運行,彼此不需要協作。

    • 而所謂并發(Concurrency),則是多個任務彼此交替執行,但是同一時間只能有一個處于運行狀態,并發執行強調任務之間的彼此協作。

    并發通常被誤解為并行,并發實際是隱式的調度獨立的代碼,以協作的方式運行。比如在等待IO線程完成IO操作之前,可以啟動IO線程之外的其他獨立代碼同步執行其他操作。

    關于并發、并行的圖示,如下:

    Python多進程,多線程及GIL全局解釋器鎖實例分析

    由于CPython解釋器中的全局解釋器鎖(GIL,Global Interpreter Lock)的存在,所以Python中的并行實際上是一種假并行,并不是真正意義上的同時獨立運行。

    2. 線程與進程的應用場景

    進程(Process)是操作系統層面的一個抽象概念,它是運行代碼創建出來的、加載到內存中運行的程序。電腦上通常運行著多個進程,這些進程之間是彼此獨立運行的。

    線程(Thread)是操作系統可以調度的最小運行程序單位,其包含在進程中,一個進程中通常至少包含1個線程,一些進程中會包含多個線程。多個線程運行的都是父進程的相同代碼,理想情況下,這些線程是并行執行的,但由于GIL的存在,所以它們實際上是交替執行的,并不是真正意義上的獨立、并行的執行的。

    下表是進程與線程的對比:

    Python多進程,多線程及GIL全局解釋器鎖實例分析

    對于IO密集型的操作,更適合使用多線程編程的方式來解決問題;對于CPU密集型的操作,則更適合使用多進程編程的方式來解決問題。

    2.1. 并行/并發編程相關的技術棧

    Python中提供了一些模塊用于實現并行/并發編程,具體如下所示:

    threading:Python中進行多線程編程的標準庫,是一個對_thread進行再封裝的高級模塊。multiprocessing:類似于threading模塊,提供的API接口也與threading模塊類似,不同的是它進行多進程編程。concurrent.futures:標準庫中的一個模塊,在線程編程模塊的基礎上抽象出來的更高級實現,且該模塊的編程為異步模式。queue:任務隊列模塊,queue中提供的隊列是線程安全的,所以可以使用這個模塊進行線程之間進行安全的數據交換操作。不支持分布式。celery:一個高級的分布式任務隊列,通過multiprocessing模塊或者gevent模塊可以實現隊列中人物的并發執行。支持多節點之間的分布式計算。 2.2. 通過編碼比較多進程與多線程的執行效果

    在下面的代碼中,定義了兩個函數:only_sleep()以及crunch_numbers(),前者用于模擬IO密集型操作(需要頻繁中斷),后者用于模擬CPU密集型操作。

    然后在串行調用、多線程的方式調用、多進程的方式調用,三種不同的執行環境中,比較各個函數的執行效率情況。

    具體代碼以及執行結果如下所示:

    import time
    import datetime
    import logging
    import threading
    import multiprocessing
    
    FORMAT = "%(asctime)s [%(processName)s %(process)d] %(threadName)s %(thread)d <=%(message)s=>"
    logging.basicConfig(format=FORMAT, level=logging.INFO, datefmt='%H:%M:%S')
    def only_sleep():
    	"""
    	模擬IO阻塞型操作,此時多線程優勢明顯
    
    	:return:
    	"""
    	logging.info('in only_sleep function')
    	time.sleep(1)
    def crunch_numbers():
    	"""
    	模擬CPU密集型操作,此時多進程優勢明顯
    	:return:
    	"""
    	logging.info('in crunch_numbers function')
    	x = 0
    	while x < 1000000:
        	x += 1
    if __name__ == '__main__':
    	work_repeat = 4
    	print('==>> only_sleep function test')
    	count = 0
    	for func in (only_sleep, crunch_numbers):
        	count += 1
    	    # run tasks serially
        	start1 = datetime.datetime.now()
    	    for i in range(work_repeat):
        	    func()
    	    stop1 = datetime.datetime.now()
        	delta1 = (stop1 - start1).total_seconds()
    	    print('Serial Execution takes {} seconds~'.format(delta1))
    
        	# run tasks with multi-threads
    	    start2 = datetime.datetime.now()
        	thread_lst = [threading.Thread(target=func, name='thread-worker' + str(i)) for i in range(work_repeat)]
    	    [thd.start() for thd in thread_lst]
        	[thd.join() for thd in thread_lst]
    	    stop2 = datetime.datetime.now()
        	delta2 = (stop2 - start2).total_seconds()
    	    print('Multi-Threads takes {} seconds~'.format(delta2))
        	# run tasks with multiprocessing
    	    start3 = datetime.datetime.now()
        	proc_lst = [multiprocessing.Process(target=func, name='process-worker' + str(i)) for i in range(work_repeat)]
    	    [thd.start() for thd in proc_lst]
        	[thd.join() for thd in proc_lst]
    	    stop3 = datetime.datetime.now()
        	delta3 = (stop3 - start3).total_seconds()
    	    print('Multi-Processing takes {} seconds~'.format(delta3))
        	if count == 1:
            	print('\n', '*.' * 30, end='\n\n')
    	        print('==>> crunch_numbers function test')

    上述代碼的執行結果如下:

    23:55:51 [MainProcess 568124] MainThread 182168 <=in only_sleep function=>
    ==>> only_sleep function test
    23:55:52 [MainProcess 568124] MainThread 182168 <=in only_sleep function=>
    23:55:53 [MainProcess 568124] MainThread 182168 <=in only_sleep function=>
    23:55:54 [MainProcess 568124] MainThread 182168 <=in only_sleep function=>
    23:55:55 [MainProcess 568124] thread-worker0 553012 <=in only_sleep function=>
    23:55:55 [MainProcess 568124] thread-worker1 567212 <=in only_sleep function=>
    23:55:55 [MainProcess 568124] thread-worker2 547252 <=in only_sleep function=>
    23:55:55 [MainProcess 568124] thread-worker3 561892 <=in only_sleep function=>
    Serial Execution takes 4.022761 seconds~
    Multi-Threads takes 1.01416 seconds~
    23:55:56 [process-worker0 563068] MainThread 567480 <=in only_sleep function=>
    23:55:56 [process-worker1 567080] MainThread 567628 <=in only_sleep function=>
    23:55:56 [process-worker2 567868] MainThread 563656 <=in only_sleep function=>
    23:55:56 [process-worker3 567444] MainThread 566436 <=in only_sleep function=>
    23:55:57 [MainProcess 568124] MainThread 182168 <=in crunch_numbers function=>
    Multi-Processing takes 1.11466 seconds~

    *.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.

    ==>> crunch_numbers function test
    23:55:57 [MainProcess 568124] MainThread 182168 <=in crunch_numbers function=>
    23:55:57 [MainProcess 568124] MainThread 182168 <=in crunch_numbers function=>
    23:55:57 [MainProcess 568124] MainThread 182168 <=in crunch_numbers function=>
    Serial Execution takes 0.1786 seconds~
    23:55:57 [MainProcess 568124] thread-worker0 567412 <=in crunch_numbers function=>
    23:55:57 [MainProcess 568124] thread-worker1 566468 <=in crunch_numbers function=>
    23:55:57 [MainProcess 568124] thread-worker2 565272 <=in crunch_numbers function=>
    23:55:57 [MainProcess 568124] thread-worker3 568044 <=in crunch_numbers function=>
    Multi-Threads takes 0.195057 seconds~
    23:55:58 [process-worker0 567652] MainThread 561892 <=in crunch_numbers function=>
    23:55:58 [process-worker1 553012] MainThread 547252 <=in crunch_numbers function=>
    23:55:58 [process-worker2 554024] MainThread 556500 <=in crunch_numbers function=>
    23:55:58 [process-worker3 565004] MainThread 566108 <=in crunch_numbers function=>
    Multi-Processing takes 0.155246 seconds~

    Process finished with exit code 0

    從上述執行結果中可以看出:

    上述代碼的執行結果也驗證了此前的結論:對于IO密集型操作,適合使用多線程編程的方式解決問題;而對于CPU密集型的操作,則適合使用多進程編程的方式解決問題。

    3. Python中的GIL是什么,它影響什么

    GIL是CPython中實現的全局解釋器鎖 (Global Interpreter Lock),由于CPython是使用最廣泛的Python解釋器,所以GIL也是Python世界中最飽受爭議的一個主題。

    GIL是互斥鎖,其目的是為了確保線程安全,正是因為有了GIL,所以可以很方便的與外部非線程安全的模塊或者庫結合起來。但是這也是有代價的,由于有了GIL,所以導致Python中的并行并不是真正意義上的并行,所以也就無法同時創建兩個使用相同代碼段的線程,相同代碼的線程只能有一個處于執行狀態。因為GIL互斥鎖,相同代碼訪問的數據會被加鎖,只有當一個線程釋放鎖之后,相同代碼的另一個線程才能訪問未被加鎖的數據。

    所以Python中的多線程是相互交替執行的,并不是真正的并行執行的。但是在CPython之外的一些庫,是可以實現真正意義上的并行的,比如numpy這個數據處理常用的庫。

    感謝各位的閱讀,以上就是“Python多進程,多線程及GIL全局解釋器鎖實例分析”的內容了,經過本文的學習后,相信大家對Python多進程,多線程及GIL全局解釋器鎖實例分析這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女