1. 程式人生 > >python之多執行緒

python之多執行緒

注:本文是廖大的教程文章,本人也在學習,因為老是記不住,自己手打一邊,程式碼也是親自測試。
廖大傳送門

多執行緒

多個任務可以由多程序完成,也可以由一個程序內的多執行緒完成。
一個執行緒由多個程序組成,一個程序至少有一個執行緒。
由於執行緒是作業系統直接支援的單元,因此,高階語言都內建多執行緒的支援,python 也不例外,並且,python 的執行緒是真正的 Posix Thread ,不是模擬出來的執行緒。
python 的標準庫提供了兩個模組:_thread 和 threading ,_thread 是低階模組,threading 是高階模組。絕大多數的情況下,我們只用 threading 就可以了。
啟動一個執行緒就是把函式傳入並建立 Thread 例項,然後呼叫 start() 函開始執行就可以了。

import time
import threading

#執行緒執行的程式碼
def loop():
    print('thread %s is running' % threading.current_thread().name)
    n = 0
    while n < 5:
        n += 1
        print('thread %s >>> %s' % (threading.current_thread().name,n))
        time.sleep(1)
    print('thread %s end' % threading.current_thread().name)

print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop,name='LoopTread')
t.start()
t.join()
print('thread %s end' % threading.current_thread().name)

執行結果

thread MainThread is running...
thread LoopTread is running
thread LoopTread >>> 1
thread LoopTread >>> 2
thread LoopTread >>> 3
thread LoopTread >>> 4
thread LoopTread >>> 5
thread LoopTread end
thread MainThread end

由於任何程序都會預設開啟一個執行緒,我們把該執行緒稱為主執行緒,主執行緒又可以開啟新的執行緒,Python 的 threading 模組有個 current_thread() 函式,它永遠返回當前執行緒的例項。主執行緒例項的名字叫 MainThread ,子執行緒的名字在建立時指定,我們用 LoopThread 命名子執行緒。名字僅僅在列印時用來顯示,完全沒有其他意義,如果不起名字 Python 就自動給執行緒命名為 Thread-1,Thread-2……

Lock

多程序和多執行緒最大的不同在於,多程序中,同一個變數,各自有一份拷貝到每個程序,互不影響,而執行緒中,所有變數都是又所有執行緒共享所有,任何一個變數都可以被任何一個執行緒修改,因此,執行緒之間共享資料最大的危險在於多執行緒同時修改同一個變數,把內容給改亂了。
舉個例子

#假定這是你的銀行存款
balance = 0

def change_it(n):
    #先存後取
    global balance
    balance += n
    balance -= n

def run_thread(n):
    for i in range(100000):
        change_it(n)

t1 = threading.Thread(target=run_thread,args=(5,))
t2 = threading.Thread(target=run_thread,args=(8,))

t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

我們定義了一個共享變數balance,初始值為0,並且啟動兩個執行緒,先存後取,理論上結果應該為0,但是,由於執行緒的排程是由作業系統決定的,當t1、t2交替執行時,只要迴圈次數足夠多,balance的結果就不一定是0了。
執行結果:

5

原因是因為高階語言的一條語句在 CPU 執行時是若干條語句,即使一個簡單的計算

balance += n

也要分兩步

  • 計算 balance + n 結果存到臨時變數中,
  • 將臨時變數的值賦給 balance

究其原因,是因為修改 balance 需要多條語句,而執行這幾條語句時,執行緒可能中斷,從而導致多個執行緒把同一個物件的內容改亂了。

兩個執行緒同時一存一取,就可能導致餘額不對,你肯定不希望你的銀行存款莫名其妙地變成了負數,所以,我們必須確保一個執行緒在修改 balance的時候,別的執行緒一定不能改。

如果我們要確保 balance 計算正確,就要給 change_it() 上一把鎖,當某個執行緒開始執行 change_it() 時,我們說,該執行緒因為獲得了鎖,因此其他執行緒不能同時執行 change_it(),只能等待,直到鎖被釋放後,獲得該鎖以後才能改。由於鎖只有一個,無論多少執行緒,同一時刻最多隻有一個執行緒持有該鎖,所以,不會造成修改的衝突。建立一個鎖就是通過threading.Lock() 來實現:

lock = threading.Lock()
def run_thread(n):
    for i in range(100000):
        #先要獲取鎖
        lock.acquire()
        try:
            #放心改吧
            change_it(n)
        finally:
            #改完記得釋放鎖哦
            lock.release()

當多個執行緒同時執行 lock.acquire() 時,只有一個執行緒能成功地獲取鎖,然後繼續執行程式碼,其他執行緒就繼續等待直到獲得鎖為止。

獲得鎖的執行緒用完後一定要釋放鎖,否則那些苦苦等待鎖的執行緒將永遠等待下去,成為死執行緒。所以我們用 try…finally 來確保鎖一定會被釋放。

  • 鎖的好處就是確保了某段關鍵程式碼只能由一個執行緒從頭到尾完整地執行。
  • 壞處當然也很多,首先是阻止了多執行緒併發執行,包含鎖的某段程式碼實際上只能以單執行緒模式執行,效率就大大地下降了。
  • 其次,由於可以存在多個鎖,不同的執行緒持有不同的鎖,並試圖獲取對方持有的鎖時,可能會造成死鎖,導致多個執行緒全部掛起,既不能執行,也無法結束,只能靠作業系統強制終止。

多核CPU

如果你不幸擁有一個多核CPU,你肯定在想,多核應該可以同時執行多個執行緒。
如果寫一個死迴圈的話,會出現什麼情況呢?
開啟Mac OS X的Activity Monitor,或者Windows的Task Manager,都可以監控某個程序的CPU使用率。
我們可以監控到一個死迴圈執行緒會100%佔用一個CPU。
如果有兩個死迴圈執行緒,在多核CPU中,可以監控到會佔用200%的CPU,也就是佔用兩個CPU核心。
要想把N核CPU的核心全部跑滿,就必須啟動N個死迴圈執行緒。
試試用Python寫個死迴圈:

import threading, multiprocessing

def loop():
    x = 0
    while True:
        x = x ^ 1

for i in range(multiprocessing.cpu_count()):
    t = threading.Thread(target=loop)
    t.start()

啟動與CPU核心數量相同的N個執行緒,在4核CPU上可以監控到CPU佔用率僅有102%,也就是僅使用了一核。

但是用C、C++或Java來改寫相同的死迴圈,直接可以把全部核心跑滿,4核就跑到400%,8核就跑到800%,為什麼Python不行呢?

因為Python的執行緒雖然是真正的執行緒,但直譯器執行程式碼時,有一個GIL鎖:Global Interpreter Lock,任何Python執行緒執行前,必須先獲得GIL鎖,然後,每執行100條位元組碼,直譯器就自動釋放GIL鎖,讓別的執行緒有機會執行。這個GIL全域性鎖實際上把所有執行緒的執行程式碼都給上了鎖,所以,多執行緒在Python中只能交替執行,即使100個執行緒跑在100核CPU上,也只能用到1個核。

GIL是Python直譯器設計的歷史遺留問題,通常我們用的直譯器是官方實現的CPython,要真正利用多核,除非重寫一個不帶GIL的直譯器。

所以,在Python中,可以使用多執行緒,但不要指望能有效利用多核。如果一定要通過多執行緒利用多核,那隻能通過C擴充套件來實現,不過這樣就失去了Python簡單易用的特點。

不過,也不用過於擔心,Python雖然不能利用多執行緒實現多核任務,但可以通過多程序實現多核任務。多個Python程序有各自獨立的GIL鎖,互不影響。

相關推薦

python執行程序併發通訊

1.獨立的程序記憶體空間與共享的伺服器程序空間 程序之間是:互不干擾的獨立記憶體空間 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time :

Python執行python執行設計同時執行個函式命令詳細攻略

Python之多執行緒:python多執行緒設計之同時執行多個函式命令詳細攻略 目的 同時執行多個函式命令   採取方法 T1、單個實現 import threading threading.Thread(target=my_record()).start(

python執行

注:本文是廖大的教程文章,本人也在學習,因為老是記不住,自己手打一邊,程式碼也是親自測試。廖大傳送門 多執行緒 多個任務可以由多程序完成,也可以由一個程序內的多執行緒完成。 一個執行緒由多個程序組成,一個程序至少有一個執行緒。 由於執行緒是作業系統直接支援的單元,因此,高

Python爬蟲執行程序

前言 我們之前寫的爬蟲都是單個執行緒的?這怎麼夠?一旦一個地方卡到不動了,那不就永遠等待下去了?為此我們可以使用多執行緒或者多程序來處理。 首先宣告一點! 多執行緒和多程序是不一樣的!一個是 thread 庫,一個是 multiprocessing 庫。而多執行緒 thread 在 Pytho

Python併發程式設計執行使用

目錄 一 開啟執行緒的兩種方式 二 在一個程序下開啟多個執行緒與在一個程序下開啟多個子程序的區別 三 練習 四 執行緒相關的其他方法 五 守護執行緒 六 Python GIL(Global Interpreter Lock) 七 同步鎖 八 死鎖現象

Python實戰執行程式設計thread模組

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Python 執行程序 (二) 執行、同步、通訊

Python 多執行緒、多程序 (一)之 原始碼執行流程、GIL Python 多執行緒、多程序 (二)之 多執行緒、同步、通訊 Python 多執行緒、多程序 (三)之 執行緒程序對比、多執行緒 一、python多執行緒 對於I/O操作的時候,程序與執行緒的效能差別不大,甚至由於執行緒更輕量級,效能更高

Python真正執行殤——GIT全域性解釋鎖

- 多執行緒的理解: 多程序和多執行緒都可以執行多個任務,執行緒是程序的一部分。執行緒的特點是執行緒之間可以共享記憶體和變數,資源消耗少(不過在Unix環境中,多程序和多執行緒資源排程消耗差距不明顯,Unix排程較快),缺點是執行緒之間的同步和加鎖比較麻煩。 Python多執行緒的缺陷:

Python中的執行程式設計,執行安全與鎖(一) 聊聊Python中的GIL 聊聊Python中的GIL python基礎執行鎖機制 python--threading執行總結 Python3入門執行threading常用方法

1. 多執行緒程式設計與執行緒安全相關重要概念 在我的上篇博文 聊聊Python中的GIL 中,我們熟悉了幾個特別重要的概念:GIL,執行緒,程序, 執行緒安全,原子操作。 以下是簡單回顧,詳細介紹請直接看聊聊Python中的GIL  GIL:&n

Python併發程式設計系列執行

1引言 2 建立執行緒   2.1 函式的方式建立執行緒   2.2 類的方式建立執行緒 3 Thread類的常用屬性和方法   3.1 守護執行緒:Deamon   3.2 join()方法 4 執行緒間的同步機制   4.1 互斥鎖:Lock   4.2 遞迴鎖:RLock   4.3

提高效率python執行

python本身的設計對多執行緒的執行有所限制。為了資料安全設計有GIL全域性直譯器鎖。在python中,一個執行緒的執行包括獲取GIL、執行程式碼直到掛起和釋放GIL。每次釋放GIL鎖,執行緒之間都會進行競爭,由拿到鎖的執行緒進入cpu執行,所以由於GIL鎖的存在,py

python高效能程式碼執行優化

以常見的埠掃描器為例項 埠掃描器的原理很簡單,操作socket來判斷連線狀態確定主機埠的開放情況。 import socket def scan(port): s = socket.socket() if s.connect_ex(('localhost', po

Python學習【第24篇】:死鎖,遞迴鎖,訊號量,Event事件,執行Queue python併發程式設計執行2------------死鎖與遞迴鎖,訊號量等

python併發程式設計之多執行緒2------------死鎖與遞迴鎖,訊號量等 一、死鎖現象與遞迴鎖 程序也是有死鎖的 所謂死鎖: 是指兩個或兩個以上

Python學習【第23篇】:利用threading模組開執行 python併發程式設計執行1

python併發程式設計之多執行緒1 一多執行緒的概念介紹 threading模組介紹 threading模組和multiprocessing模組在使用層

Python實戰執行程式設計threading Thread

                在Python中可以使用繼承threading.Thread類來實現多執行緒程式設計,其中子類可以重寫父類的__init__和run方法來實現使用者執行緒的邏輯,如下是一個簡單的多執行緒類實現[python] view plain copy print?import threa

Python 執行程序 (二) 執行、同步、通訊

一、python多執行緒 對於I/O操作的時候,程序與執行緒的效能差別不大,甚至由於執行緒更輕量級,效能更高。這裡的I/O包括網路I/O和檔案I/O 1、例項 假如利用socket傳送http請求,也就是網路I/O。爬取列表網頁中的寫href連結,然後獲取href連結之後,在爬去連結的網頁詳情。 如果不適用

python併發程式設計執行理論部分

一 什麼是執行緒     在傳統作業系統中,每個程序有一個地址空間,而且預設就有一個控制執行緒   執行緒顧名思義,就是一條流水線工作的過程,一條流水線必須屬於一個車間,一個車間的工作過程是一個程序       車間負責把資源整合到一起,是一個資源單位,而一個車間內至少有一個流水線       流水線

Python網路程式設計執行

多執行緒 多執行緒舉例: import threading from time import sleep,ctime def sing(): for i in range(3): print("正在唱歌...%d"%i)

Python 3 執行研究

今天想寫一個工具,通過多執行緒去一個佇列中讀取資料,要求如下: 1.多個執行緒同時讀取佇列,所以佇列要做到執行緒安全:queue.Queue,這個本身就是執行緒安生的,所以沒有問題 2.主執行緒要等到所有新開的子執行緒結束後才能結束,這個用到了Threading中的isAl

python爬蟲執行queue

        首先先來介紹下queue這個包吧,這個包叫佇列,沒錯,就是那個和棧反過來的那個佇列,大家一聽佇列就隨口說出先進先出,而棧則是後進先出,為什麼要用用佇列來實現,其實我也不知道,反正用過之後很順手,具體哪裡也說不上來         先來看下佇列的內建方法的,我