python多線程和多進程

Table of Contents

  1. 線程概念
    1. 進程與線程的區別
      1. 線程常用方法
    2. 線程常用方法
      1. 普通創建方式
      2. 重寫類,來實現啟動線程
      3. 其他方法
    3. GIL
      1. python多線程的工作流程
    4. 線程鎖
    5. 互斥鎖(mutex)
    6. 遞歸鎖
    7. 信號量(BoundedSemphore)
    8. Event事件
    9. 條件(Condition類)
    10. 定時器
  2. 多進程
    1. 什么是多進程
    2. 相關用法
    3. 進程間的通信
      1. 使用Queue
      2. 使用Pipe
    4. 進程池

多線程

<a id="orgacb2cbe"></a>

線程概念

所謂線程是操作系統能夠進行運算調度的最小單元,一個線程是一個執行上下文,是cpu執行命令時的一串指令,一個進程能夠并發出多個線程,來執行不同的任務。

<a id="orga1fd5b1"></a>

進程與線程的區別

1.同一進程中的線程共享同一內存空間,但進程之間是獨立的。
2.同一進程中的線程共享數據,但是進程之間的數據是獨立的。
3.同一進程中的線程可以很方便地通信,但是進程之間通信之間的通信則需要通過代理實現。
4.在一個進程中創建一個線程很容易,但是進程創建一個子進程,則fork一個出來,復制一份內存空間
5.主線程可以操縱同一進程中的其他線程,但是進程只能操縱它的子進程。線程啟動快,進程啟動慢。線程之間切換,比較耗時,比較占用cpu,比較適合io密集型任務,
而進程之間的切換比較快速,但是進程fork出很多子進程,比較占用內存空間。對主線程的修改可能會對其他線程造成影響,但是進程和進程,進程和子進程,都是一
個獨立的內存空間,所以一個進程的修改,不會對其他進程造成影響。

<a id="orgfd4eec7"></a>

線程常用方法

<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">

<colgroup>
<col class="org-left" />

<col class="org-left" />
</colgroup>
<thead>
<tr>
<th scope="col" class="org-left">方法</th>
<th scope="col" class="org-left">注釋</th>
</tr>
</thead>

<tbody>
<tr>
<td class="org-left">start()</td>
<td class="org-left">線程如果已經準備,啟用該方法,則開始啟動線程</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">setName()</td>
<td class="org-left">為線程設置名稱</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">join()</td>
<td class="org-left">逐個執行每個進程,主線程會等到子線程全部執行完畢,在退出</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">run()</td>
<td class="org-left">可以重寫類時,重寫run方法,起到自定義線程類的作用</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">setDaemon(True)</td>
<td class="org-left">設置為守護進程</td>
</tr>
</tbody>
</table>

<a id="orgc9bc3d0"></a>

線程常用方法

<a id="orgd730348"></a>

普通創建方式

import threading
import time
def run(n):
        print("task", n)
        sleep(1)

t1 = threading.Thread(target=run, args=('t1',))
t2 = threading.Thread(target=run, args=('t2',))
t3 = threading.Thread(target=run, args=('t3',))

t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()

<a id="orge37d9b1"></a>

重寫類,來實現啟動線程

class MyThread(threading.Thread):
        def __init__(self, n):
                super().__init__()
                self.n = n

        def run(self, self.n):
                print("task", n)
                time.sleep(1)
                print("1s")
                time.sleep(1)
                print("2s")

if __name__ == "__main__":
        t1 = MyThread("one")
        t2 = MyThread("two")
        t1.start()
        t2.start()

<a id="orgb228d0f"></a>

其他方法

  • threading.currentthread(): 輸出當前線程的名稱
  • threading.activecount(): 輸出當前活躍線程的個數
  • t.setDaemon(True): 設置當前線程為守護進程,只要主進程一退出,子進程也隨之退出, 必須在調用start()之前使用,否則會報RuntimeError的錯誤

<a id="orgb8fbe80"></a>

GIL

1.GIL全稱為Global Interpreter Lock(全局解釋器鎖),全局解釋器鎖的存在,保證了同一進程中,只能有一個線程正在運行,所以python多線程編程效率并沒有那么高,
通過使用多進程的方式,能夠有效地提升效率。python2中GIL是通過ticks來計數,計數值到達100,則解釋器,就會釋放,但是在python3中,GIL是通過計時來實現了,只要時間一到,不管任務完沒完成,都會收回
GIL(這種情況下,對cpu密集型運算更有利了一些,但依然問題很大),這種情況下,python對cpu密集型任務并不友好,而對io密集型任務比較友好。
2.GIL在cpython才存在,jpython和pypy中是沒用GIL,因為cpython調用的是c語言的原生線程

<a id="org7c0db1d"></a>

python多線程的工作流程

ipython使用多線程的時候,是使用c語言原生線程
1.拿到公共數據
2.申請GIL
3.python解釋器調用os原生線程
4.os執行cpu運算
5.時間到了收回GIL,無論是否完成任務
6.如此循環以上過程
7.當其他線程完成后,就繼續執行之前的線程(從之前線程執行的上下文處開始執行),每個線程都執行自己的運算,時間到了就切換線程

<a id="org7bb53a5"></a>

線程鎖

因為線程之間是公用同一份數據,所以當多個線程對同一數據進行操作時,容易產生臟數據,導致數據出現錯誤,所以出現了線程鎖的概念,在一個線程擁有該鎖時,其他線程不能進行操作,保證了,同一數據在同一時刻,
只有一個線程對其進行操作

<a id="orge4ca630"></a>

互斥鎖(mutex)

為了防止上述情況的發生,所以有了互斥鎖

import threading
# 實例化了一個鎖對象
lock = threading.Lock()
def run():
        lock.acquire()  # 獲得鎖,其他線程不能操作
        print("hello world")
        lock.release()  # 釋放鎖,其他線程開始競爭

<a id="orga4ec85d"></a>

遞歸鎖

與上面的Lock用法類似,RLock可以嵌套,實現多個鎖

import threading
rlock = threading.RLock()
def run():
        rlock.acquire()
        rlock.acquire()
        print("hello world")
        rlock.release()
        print("hello syk")
        rlock.release()

<a id="orgf9efecd"></a>

信號量(BoundedSemphore)

互斥鎖只允許一個線程修改數據,信號量則允許多個線程同時修改,就比如廁所有三個坑,只有其中的坑空出來了,才能有人進去

import threading
semphore = threading.BoundedSemphore(5)  # 表示最多有五個線程同時進行操作
def run(i):
        semphore.acquire()
        print("hello",i)

for i in range(20):
        t = threading.Thread(target=run)
        t.start()

while t.active_count != 1:
        pass
else:
        print("all thread has been done")

<a id="org9255bad"></a>

Event事件

python線程的事件主要是用于主線程控制其他線程的執行,event是一個簡單的事件同步對象,提供了一下幾種方法:

<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">

<colgroup>
<col class="org-left" />

<col class="org-left" />
</colgroup>
<thead>
<tr>
<th scope="col" class="org-left">方法</th>
<th scope="col" class="org-left">注釋</th>
</tr>
</thead>

<tbody>
<tr>
<td class="org-left">set()</td>
<td class="org-left">將flag置為True</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">clear()</td>
<td class="org-left">將flag置為False</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">isset()</td>
<td class="org-left">是否將flag設置為True</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">wait()</td>
<td class="org-left">一直監聽flag,如果沒有監聽到,則一直為阻塞狀態</td>
</tr>
</tbody>
</table>

<a id="org034b100"></a>

條件(Condition類)

使得線程等待,滿足條件才將n個線程釋放

<a id="org70af908"></a>

定時器

定時器,指定n秒后執行操作

import threading
def hello():
        print("hello world")

t = threading.Timer(1, hello)
t.start()

<a id="org06e9300"></a>

多進程

<a id="org4ad204e"></a>

什么是多進程

1.一個程序的執行實例就是一個進程,進程提供一個程序運行所需要的所有資源(進程本質上是資源的集合)
2.每一個進程都有自己的虛擬地址空間,可執行的代碼塊,操作系統接口,安全的上下文(記錄啟動該進程的用戶和權限),還有唯一的進程ID,
環境變量,優先級,最大最小的工作空間(內存空間)。

與進程相關的資源:
1.內存頁(統一進程下的線程共享同一內存空間)
2.文件描述符
3.安全憑證(啟動該進程的用戶ID)

<a id="org7331068"></a>

相關用法

from multiprocessing import Process
import time


def hello():
     print("helo world")


if __name__ == "__main__":
     p = Process(target=hello)
     p.start()
     p.join()

<a id="org5b3cf39"></a>

進程間的通信

由于進程間的數據是獨立的,所以進程間的通信可以通過Queue和Pipe來實現

<a id="org9d70940"></a>

使用Queue

from  multiprocessing import Process, Queue
def q(p):
        p.put([41, None, 'hello'])

if __name__ == "__main__":
     p = Queue()
     t = Process(target=q, args=(p,))
     t.start()
     print(q.get())
     t.join()

<a id="org179cdd3"></a>

使用Pipe

Pipe的實質是數據的傳遞而不是數據的共享,在管道的兩頭都有一個recv和send方法,負責接受和發送,
但是如果兩端同時進行相同的操作,那么就會發生錯誤,破壞管道中的數據。

from multiprocessing import Process, Pipe
import time


def parent_pro(conn):
        conn.send("hello world, my son")
        conn.close()


def child_pro(conn):
        data = conn.recv()
        print(data)
        conn.close()


if __name__ == "__main__":

        parent_pipe, child_pipe = Pipe()
        p1 = Process(target=parent_pro, args=(parent_pipe,))
        p2 = Process(target=child_pro, args=(child_pipe,))
        p1.start()
        p2.start()
        p1.join()
        p2.join()

<a id="org475381f"></a>

進程池

因為多進程比較消耗內存空間,所以在使用多進程時,一般可以使用進程池(用來防止內存被大量消耗), (多線程不需要這種概念,多線程的啟動消耗比較小,但是多線程切換比較消耗cpu資源)

常用方法:
1.apply(): 同步執行(串行)
2.applyasync(): 異步執行(并行)
3.terminate(): 立即關閉進程池
4.join(): 主進程等待所有子進程執行完畢,必須在close()和terminate()之后
5.close(): 待所有進程執行完畢之后,關閉進程池

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容

  • 又來到了一個老生常談的問題,應用層軟件開發的程序員要不要了解和深入學習操作系統呢? 今天就這個問題開始,來談談操...
    tangsl閱讀 4,145評論 0 23
  • 一、進程和線程 進程 進程就是一個執行中的程序實例,每個進程都有自己獨立的一塊內存空間,一個進程中可以有多個線程。...
    阿敏其人閱讀 2,620評論 0 13
  • 有那么一刻我很有聯系你的沖動 想假裝發錯消息給你 我的拳頭在手心握出深深的痕跡 我只是太孤獨
    珠珠愛吃素閱讀 198評論 0 0
  • 這是今天她第三次打她的孩子。 第一次。 孩子想坐搖搖車,搖搖車被幾個大一些的孩子圍著,她的孩子擠不進去,哭。 她抱...
    我就是那個郭郭啦閱讀 439評論 6 4
  • 這是萌面的大貓第三天來到簡書,也是從今天開始更新文章和隨筆,以后會每天堅持早起,并不一定可以每天更新文章,堅持一年...
    萌面的大貓閱讀 145評論 0 0