Python3教程:多線程的使用教程

来源:https://www.cnblogs.com/xxpythonxx/archive/2022/06/21/16398059.html
-Advertisement-
Play Games

1、Collections sort(List list) 自然升序排序 reverse(List<?> list) 集合反轉 binarySearch(List<? extends Comparable<? super T>> list, T key) 二分查找(要求集合有序) addAll(Co ...


一、簡介

Python中使用線程有兩種方式:函數或者用類來包裝線程對象。

函數式:調用 _thread 模塊中的start_new_thread()函數來產生新線程。語法如下:

參數說明:

  • function - 線程函數。
  • args - 傳遞給線程函數的參數,他必須是個tuple類型。
  • kwargs - 可選參數。

實例:

import _thread
import time

# 為線程定義一個函數
def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print ("%s: %s" % ( threadName, time.ctime(time.time()) ))

# 創建兩個線程
try:
   _thread.start_new_thread( print_time, ("Thread-1", 2, ) )
   _thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
   print ("Error: 無法啟動線程")

while 1:
   pass

二、線程模塊

Python3 通過兩個標準庫_threadthreading提供對線程的支持。

_thread 提供了低級別的、原始的線程以及一個簡單的鎖,它相比於 threading 模塊的功能還是比較有限的。

threading 模塊除了包含 _thread 模塊中的所有方法外,還提供的其他方法:

  • threading.currentThread(): 返回當前的線程變數。
  • threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動後、結束前,不包括啟動前和終止後的線程。
  • threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。

除了使用方法外,線程模塊同樣提供了Thread類來處理線程,Thread類提供了以下方法:

  • run(): 用以表示線程活動的方法。
  • start():啟動線程活動。
  • join([time]): 等待至線程中止。這阻塞調用線程直至線程的join() 方法被調用中止-正常退出或者拋出未處理的異常-或者是可選的超時發生。
  • isAlive(): 返回線程是否活動的。
  • getName(): 返回線程名。
  • setName(): 設置線程名。

三、使用 threading 模塊創建線程

可以通過直接從 threading.Thread 繼承創建一個新的子類,並實例化後調用 start() 方法啟動新線程,即它調用了線程的 run() 方法:

import threading
import time

exitFlag = 0

class myThread (threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):
        print ("開始線程:" + self.name)
        print_time(self.name, self.counter, 5)
        print ("退出線程:" + self.name)

def print_time(threadName, delay, counter):
    while counter:
        if exitFlag:
            threadName.exit()
        time.sleep(delay)
        print ("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1

# 創建新線程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 開啟新線程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("退出主線程")

四、線程同步

如果多個線程共同對某個數據修改,則可能出現不可預料的結果,為了保證數據的正確性,需要對多個線程進行同步。

使用 Thread 對象的 Lock 和 Rlock 可以實現簡單的線程同步,這兩個對象都有 acquire 方法和 release 方法,對於那些需要每次只允許一個線程操作的數據,可以將其操作放到 acquire 和 release 方法之間。如下:

註意:

多線程的優勢在於可以同時運行多個任務(至少感覺起來是這樣)。但是當線程需要共用數據時,可能存在數據不同步的問題。

考慮這樣一種情況:一個列表裡所有元素都是0,線程"set"從後向前把所有元素改成1,而線程"print"負責從前往後讀取列表並列印。

那麼,可能線程"set"開始改的時候,線程"print"便來列印列表了,輸出就成了一半0一半1,這就是數據的不同步。為了避免這種情況,引入了鎖的概念。

鎖有兩種狀態——鎖定和未鎖定。每當一個線程比如"set"要訪問共用數據時,必須先獲得鎖定;如果已經有別的線程比如"print"獲得鎖定了,那麼就讓線程"set"暫停,也就是同步阻塞;等到線程"print"訪問完畢,釋放鎖以後,再讓線程"set"繼續。

經過這樣的處理,列印列表時要麼全部輸出0,要麼全部輸出1,不會再出現一半0一半1的尷尬場面。

實例:

#!/usr/bin/python3

import threading
import time

class myThread (threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):
        print ("開啟線程: " + self.name)
        # 獲取鎖,用於線程同步
        threadLock.acquire()
        print_time(self.name, self.counter, 3)
        # 釋放鎖,開啟下一個線程
        threadLock.release()

def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print ("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1

threadLock = threading.Lock()
threads = []

# 創建新線程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 開啟新線程
thread1.start()
thread2.start()

# 添加線程到線程列表
threads.append(thread1)
threads.append(thread2)

# 等待所有線程完成
for t in threads:
    t.join()
print ("退出主線程")

五、線程優先順序隊列( Queue)

1、queue隊列

1.python3中的隊列模塊是queue,不是Queue

2.一般涉及到同步,多線程之類用到隊列模塊

3.定義了 queue.Queue 類,以及繼承它的 queue.LifoQueue 類 和 queue.PriorityQueue 類 和 queue.SimpleQueue 類

4.分別對應隊列類(FIFO先進先出),LIFO後進先出隊列類,優先隊列,無邊界FIFO簡單隊列類

5.還有兩個異常:隊滿和隊空

2、隊列queue公共方法

'''
學習中遇到問題沒人解答?小編創建了一個Python學習交流群:857662006
尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書!
'''
import queue

#創建基本隊列
#queue.Queue(maxsize=0)創建一個隊列對象(隊列容量),若maxsize小於或者等於0,隊列大小沒有限制
Q=queue.Queue(10)
print(Q)
print(type(Q))

#1.基本方法
print(Q.queue)#查看隊列中所有元素
print(Q.qsize())#返回隊列的大小
print(Q.empty())#判斷隊空
print(Q.full())#判斷隊滿

#2.獲取隊列,0--5
#Queue.put(item,block = True,timeout = None )將對象放入隊列,阻塞調用(block=False拋異常),無等待時間
for i in range(5):
    Q.put(i)
# Queue.put_nowait(item)相當於 put(item, False).


#3.讀隊列,0--5
#Queue.get(block=True, timeout=None)讀出隊列的一個元素,阻塞調用,無等待時間
while not Q.empty():
    print(Q.get())
# Queue.get_nowait()相當於get(False).取數據,如果沒數據拋queue.Empty異常


#4.另兩種涉及等待排隊任務的方法
# Queue.task_done()在完成一項工作後,向任務已經完成的隊列發送一個信號
# Queue.join()阻止直到隊列中的所有項目都被獲取並處理。即等到隊列為空再執行別的操作

3、其他

1.LifoQueue: LIFO後進先出

2.PriorityQueue:優先順序隊列,如果數據元素不具有可比性,則可將數據包裝在忽略數據項的類中,僅比較優先順序編號

3.SimpleQueue:簡單隊列,無跟蹤任務的功能

六、Queue詳細參數和用法實例

Queue 模塊中的常用方法:

  • Queue.qsize() 返回隊列的大小
  • Queue.empty() 如果隊列為空,返回True,反之False
  • Queue.full() 如果隊列滿了,返回True,反之False
  • Queue.full 與 maxsize 大小對應
  • Queue.get([block[, timeout]])獲取隊列,timeout等待時間
  • Queue.get_nowait() 相當Queue.get(False)
  • Queue.put(item) 寫入隊列,timeout等待時間
  • Queue.put_nowait(item) 相當Queue.put(item, False)
  • Queue.task_done() 在完成一項工作之後,Queue.task_done()函數向任務已經完成的隊列發送一個信號
  • Queue.join() 實際上意味著等到隊列為空,再執行別的操作

實例:

import queue
import threading
import time

exitFlag = 0

class myThread (threading.Thread):
    def __init__(self, threadID, name, q):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.q = q
    def run(self):
        print ("開啟線程:" + self.name)
        process_data(self.name, self.q)
        print ("退出線程:" + self.name)

def process_data(threadName, q):
    while not exitFlag:
        queueLock.acquire()
        if not workQueue.empty():
            data = q.get()
            queueLock.release()
            print ("%s processing %s" % (threadName, data))
        else:
            queueLock.release()
        time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1

# 創建新線程
for tName in threadList:
    thread = myThread(threadID, tName, workQueue)
    thread.start()
    threads.append(thread)
    threadID += 1

# 填充隊列
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()

# 等待隊列清空
while not workQueue.empty():
    pass

# 通知線程是時候退出
exitFlag = 1

# 等待所有線程完成
for t in threads:
    t.join()
print ("退出主線程")

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

-Advertisement-
Play Games
更多相關文章
  • 今天,來實現這樣一個有意思的交互效果: 將原本的滑鼠指針樣式,修改成自己想要的效果,並且添加上一些特殊的交互效果。 修改滑鼠樣式 首先,第一個問題,我們可以看到,上圖中,滑鼠指針的樣式被修改成了一個圓點: 正常而言應該是這樣: 當然,這裡比較簡單,在 CSS 中,我們可以通過 cursor 樣式,對 ...
  • 本章是系列文章的第七章,終於來到了鼎鼎大名的SSA,SSA是編譯器領域最偉大的發明之一,也是影響最廣的發明。 本文中的所有內容來自學習DCC888的學習筆記或者自己理解的整理,如需轉載請註明出處。周榮華@燧原科技 7.1 控制流圖回顧 對下麵的c代碼保存成7.1.cc: 1 int max(int ...
  • #數據類型 P23 ##(強類型語言:要求變數的使用要嚴格符合規定,所有變數都必須先定義後使用。) ##整數類型: ###byte占1個位元組 範圍:(-128127) ###short占2個位元組 範圍:(-3276832767) ###int占4個位元組 範圍:(-214748364821474836 ...
  • 一個美女面試官坐到我的對面,發光logo的MacBook也擋不住她那圓潤可愛的臉龐。 程式媛本就稀有,美女面試官更是難尋。具體長什麼樣呢?就像下麵這樣: 這麼溫柔可愛的面試官,應該不會為難我吧。嗯,應該是的,畢竟我這麼帥氣,面試可能就是走個過場。美女面試官是不是單身?畢竟程式員都不善交流,因為我也是 ...
  • 引言:今天閱讀前人源碼,看見一個提取文件名字的功能,用了string的函數折騰了一大堆,結果一查MsDoc,微軟原來早都提供了5個用於提取文件名稱的函數。 個人技術博客(文章整理+源碼): https://zobolblog.github.io/LearnWinAPI/ 最終效果: 1.CFileD ...
  • 點擊藍色“程式員黃小斜”關註我喲 加個“星標”,每天和你一起多進步一點點! 今天給大家分享一篇有意思的爽文,但也是根據多年之前一個真實報道改編而來的。 本文字數較多,建議先收藏,上下班路上、帶薪上廁所、渾水摸魚時再慢慢看~ 來源: https://www.zhihu.com/question/483 ...
  • Spring5——JdbcTemplate筆記 概念 template,翻譯過來是模板的意思,顧名思義,JdbcTemplate就是一個JDBC的模板,它對JDBC進行了封裝,可以很方便地實現對資料庫的CRUD(增、刪、改、查)的操作。 JdbcTemplate準備工作 引入相關的依賴 druid- ...
  • 給大家推薦八個非常實用的Python案例,希望大家看過後能夠有所收穫! 1、合併兩個字典 Python3.5之後,合併字典變得容易起來,我們可以通過**符號解壓字典,並將多個字典傳入{}中,實現合併。 def Merge(dict1,dict2): res = {**dict1,**dict2} r ...
一周排行
    -Advertisement-
    Play Games
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...