進程、線程、協程和GIL(二)

来源:https://www.cnblogs.com/ss-py/archive/2019/01/10/10238067.html
-Advertisement-
Play Games

上一篇博客講了進程、線程、協程和GIL的基本概念,這篇我們來說說在以下三點: 1> python中使用threading庫來創建線程的兩種方式 2> 使用Event對消來判斷線程是否已啟動 3> 使用Semaphore和BoundedSemaphore兩個類分別來控制線程的併發數以及二者之間的區別。 ...


    上一篇博客講了進程、線程、協程和GIL的基本概念,這篇我們來說說在以下三點:

  1> python中使用threading庫來創建線程的兩種方式

  2> 使用Event對消來判斷線程是否已啟動

  3> 使用Semaphore和BoundedSemaphore兩個類分別來控制線程的併發數以及二者之間的區別。

  如果想要瞭解基本概念,請移步我的上一篇博客:https://www.cnblogs.com/ss-py/p/10236125.html

正文:

  利用threading庫來在創建一個線程:

from threading import Thread

def run(name):
    print('我是: %s' % (name))
    
if __name__ == '__main__':
    t = Thread(target=run, args=('線程1號',))
    t.start()

  運行結果:  

  

  首先,創建一個Thread線程,並用target=run來指定這個線程需要執行那個run方法,然後將run方法所需的參數以args參數的形式傳遞過去,

  請註意,args所傳的參數是一個元組(tuple)類型,因此即使元組中只有一個參數,也要在這個參數後再加一個逗號,如 args=('線程1號',)

  

  而且,當我們創建一個線程對象後,這個線程並不會立即執行,除非調用它的star()方法,當調用star()方法後,這個線程會調用你使用target傳給他的函數,

  並將args中的參數傳遞給這個函數。

  Python中的線程會在一個單獨的系統級線程中執行(如一個POSIX線程或一個Windows線程),這些線程全部由操作系統進行管理,線程一旦啟動,

  它將獨立運行直至目標函數運行結束。

  我們可以調用is_alive()方法來進行判斷該線程是否在運行(is_alive()方法返回True或者False):

  我們知道,進程是依賴於線程來執行的,所以當我們的py文件在執行時,我們可以理解為有一個主線程在執行,當我們創建一個子線程時,就相當於當前程式一共有兩個線程在執行。當子線程被創建後,主線程和子線程就獨立運行,相互並不影響。

  代碼如下:

 1 import time
 2 from threading import Thread
 3 
 4 def run(name):
 5     time.sleep(2)
 6     print('我是: %s' % (name))
 7 
 8 if __name__ == '__main__':
 9     t = Thread(target=run, args=('子線程',))
10     t.start()
11     print('我是主線程')

  執行結果:

  

  在代碼的第9行,創建了一個線程t,讓它來執行run()方法,這時,程式中就有了兩個線程同時存在、各自獨立運行,預設的,主線程是不會等待子線程的運算結果的,所以主線程繼續向下執行,列印L“我是主線程”,而子線程在調用run()方法時sleep了兩秒鐘,之後才列印出“我是子線程”

  當然,我們可以手動的調用join()方法來讓主線程等待子線程運行結束後再向下執行:

 1 import time
 2 from threading import Thread
 3 
 4 def run(name):
 5     time.sleep(2)
 6     print('我是: %s' % (name))
 7 
 8 if __name__ == '__main__':
 9     t = Thread(target=run, args=('子線程',))
10     t.start()
11     t.join()
12     print('我是主線程')

  

  這時,程式在進行到第十行後,主線程就卡住了,它在等待子線程運行結束,檔子線程運行結束後,主線程才會繼續向下運行,直至程式退出。

  但是,但是,無論主線程等不等待子線程,Python解釋器都會等待所有的線程都終止後才會退出。也就是說,無論主線程等不等待子線程,這個程式最終都

  會運行兩秒多,因為子線程sleep了兩秒。

  所以,當遇到需要長時間運行的線程或者是需要一直在後臺運行的線程時,可以將其設置為後臺線程(守護線程)daemon=True,如:

t = Thread(target=run, args=('子線程',), daemon=True)

  守護線程,顧名思義是守護主線程的線程,他們是依賴於主線程而存在的,當主線程執行結束後,守護線程會被立即註銷,無論該線程是否執行結束。

  當然,我們也可以利用join()來使主線程等待主線程。

  使用threading庫來創建線程還有一種方式:

from threading import Thread

class CreateThread(Thread):

    def __init__(self):
        super().__init__()

    def run(self):
        print('我是子線程!')

t = CreateThread()
t.start()

  在開始t = Thread(target=run, args=('子線程',))這種方式調用方法時子線程調用的run方法的這個方法名是我隨便起的,實際上叫什麼都行,

  但是以繼承Thread類方式實現線程時,線程調用的方法名必須是run() 這個是程式寫死的。

 

   使用Event對象判斷線程是否已經啟動

   threading庫中的Event對象包含一個可由線程來設置的信號標誌,它允許線程等待某些事件的發生。

  初始狀態時,event對象中的信號標誌被設置為假,如果有一個線程等待event對象,且這個event對象的標誌為假,那麼這個線程就會一直阻塞,直到該標誌為真。如果將一個event對象的標誌設置為真,他將喚醒所有等待這個標誌的線程,如果一個線程等待一個被設置為真得Event對象,那麼它將忽略這個事件,繼續向下執行。  

  Event (事件) 定義了一個全局的標誌Flag,如果Flag為False,當程式執行event.wait()時就會阻塞,當Flag為True時,程式執行event.wait()時便不會阻塞:

  event.set():  將標誌Flag設置為True, 並通知所有因等待該標誌而處於阻塞狀態的線程恢復運行。

  event.clear(): 將標誌Flag設置為False

  event.wait():  判斷當前標誌狀態,如果是True則立即返回,否則線程繼續阻塞。

  event.isSet(): 獲取標誌Flag狀態: 返回True或者False

 1 from threading import Thread, Event
 2 
 3 def run(num, start_evt):
 4     if int(num) >10:
 5         start_evt.set()
 6     else:
 7         start_evt.clear()
 8 start_evt = Event()
 9 
10 if __name__ == '__main__':
11     num = input("請輸入數字>>>")
12     t = Thread(target=run, args=(num, start_evt,))
13     t.start()
14     start_evt.wait()  # 主線程獲取Event對象的標誌狀態,若為True,則主線程繼續執行,否則,主線程阻塞
15     print("主線程繼續執行!")

  上邊這段代碼:當輸入的數字大於10時,將標誌設置為True,主程式繼續執行,當小於或者等於10時,將標誌設為False(預設為False),主線程阻塞。

     

  值得註意的是:當Event對象的標誌被設置為True時,他會喚醒所有等待他的線程,如果只想喚醒某一個線程,最好使用信號量。

 

  信號量

  信號量,說白了就是一個計數器,用來控制線程的併發數,每次有線程獲得信號量的時候(即acquire())計數器-1,釋放信號量時候(release())計數器+1,計數器為0的時候其它線程就被阻塞無法獲得信號量

  acquire()   # 設置一個信號量

  release()   # 釋放一個信號量

  python中有兩個類實現了信號量:(Semaphore和BoundedSemaphore)

  Semaphore和BoundedSemaphore的相同之處

    通過: threading.Semaphore(3) 或者 threading.BoundedSemaphore(3) 來設置初始值為3的計數器

    執行acquire() 計數器-1,執行release() 計數器+1,當計數器為0時,其他線程均無法再獲得信號量從而阻塞

import threading

se = threading.BoundedSemaphore(3)

for i in range(5):
    se.acquire()
    print('信號量被設置')

for j in range(10):
    se.release()
    print('信號量被釋放了')

  執行結果:   

import threading

se = threading.Semaphore(3)

for i in range(5):
    se.acquire()
    print('信號量被設置')

for j in range(10):
    se.release()
    print('信號量被釋放了')

  執行結果:

  可以看到,無論我們用那個類創建信號量,當計數器被減為0時,其他線程均會阻塞。

  這個功能經常被用來控制線程的併發數

  沒有設置信號量:

import time
import threading

num = 3

def run():
    time.sleep(2)
    print(time.time())

if __name__ == '__main__':
    t_list = []
    for i in range(20):
        t = threading.Thread(target=run)
        t_list.append(t)
    for i in t_list:
        i.start()

  執行結果:20個線程幾乎在同時執行,,如果主機在執行IO密集型任務時執行這種程式時,主機有可能會宕機,

  但是在設置了信號量時,我們可以來控制同一時間同時運行的線程數:

import time
import threading

num = 3

def run():
    se.acquire()  # 添加信號量
    time.sleep(2)
    print(time.time())
    se.release()  #  釋放一個信號量


if __name__ == '__main__':
    t_list = []
    se = threading.Semaphore(5)  # 設置一個大小為5的計數器(同一時間,最多允許5個線程在運行)
   # se = threading.BoundedSemaphore(5) 
    for i in range(20):
        t = threading.Thread(target=run)
        t_list.append(t)
    for i in t_list:
        i.start()

  這時,我們給程式加上信號量,控制它在同一時間內,最多只有5個線程在運行。

  

  兩者之間的差異性:

    當計數器達到設定好的上線時,BoundedSemaphore就無法進行release()操作了,Semaphore沒有這個限制,它會拋出異常。

  

import threading


se = threading.Semaphore(3)

for i in range(3):  # 將計數器值減為0
    se.acquire(3)

for j in range(5):  # 將計數器值加至5
    se.release()
    print('信號量被釋放了')

  運行結果:

  

import threading


se = threading.BoundedSemaphore(3)

for i in range(3):  # 將計數器值減為0
    se.acquire(3)

for j in range(5):  # 將計數器值加至5
    se.release()
    print('信號量被釋放了')

  運行結果:

  拋異常了:信號量被釋放太多次。。。

   好了,這篇文章的就先寫到這裡,下一篇文章我會講解關於線程間通信、線程加鎖等問題

  想瞭解更多關於Python、爬蟲的信息,歡迎關註我的個人微信公眾號:

  

 

 

   

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6441(本題來源於2018年中國大學生程式設計競賽網路選拔賽) 題意:輸入n和a,求滿足等式a^n+b^n=c^n的b,c的值 思路: 首先我們要知道什麼是費馬大定理 百度詞條 費馬大定理,又被稱為“費馬 ...
  • 前言 今天在老師教Session的時候,利用了Session可持久化保存伺服器端的特性嘗試做了一下用HashMap嵌套的購物車(沒有將購物車的商品信息保存在資料庫中),之所以做的這麼麻煩是為了鞏固之前學習的Map知識和鍛煉邏輯能力,正好也在其中遇到了一個關於HashMap 的問題,在此做個小小的記錄 ...
  • 背景:今天有個需求,傳人多個攝像頭ID,然後調用介面,開啟這些攝像頭的監控任務。 方法一: 如果你的攝像頭監控任務格式為: {camera_id_list=[{createBy=tjt, cameraId=camera01, startTime=2019-1-10 22:58:19}, {creat ...
  • 前兩天更新完,挺興奮 趁著興奮把虛擬機裡面的MACOSX從10.12.6升級到了10.14 然後裝XCODE,雖然比較熟悉了,但是架不住慢啊 先下載了一個DMG的鏡像文件,用不了,轉成ISO也不行 然後又找了一個CDR的才行,一個鏡像就7個G 下載一小時,往虛擬機裡面裝也差不多, 然後下載XCODE ...
  • 問題:在上一篇 "繼承與多態 文本查詢的小例子(智能指針版本)" 在Query類里使用的是智能指針,只把智能指針換成普通的指針,並不添加拷貝構造方法,會發生什麼呢? 執行時,代碼崩掉。 分析下麵一行代碼: 1,首先調用Query(string)的構造函數,把Query的成員q指向了new WordQ ...
  • 看了一個垃圾程式的架構,mmp真坑,自己費了一點功夫才搞定,就直接記錄下吧,這個是windows簡單的應用程式,但是裡面有點複雜,我們需要首先建立一個基於mfc的appwinzard程式,(憑記憶寫的,不知道單詞有沒有錯誤),然後我們直接在winmain類中添加下麵的變數# include# def... ...
  • 一、開始: 下載地址:http://nodejs.cn/download/ 下載安裝: 直到 二、打開CMD,檢查是否正常 在安裝目錄里新增兩個文件夾 然後運行命令:如下圖: npm config set prefix "D:\InstallSoftWare\nodejs\node_global" ...
  • 為了更好的理解繼承和多態,做一個文本查詢的小例子。 介面類:Query有2個方法。 eval:查詢,返回查詢結果類QueryResult rep:得到要查詢的文本 客戶端程式的使用方法: 介面類:Query,有一個私有的父類Query_base的智能指針。 父類Query_base有子類WordQu ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...