天天看點

通俗易懂:說說 Python 裡的線程安全、原子操作

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

在并發程式設計時,如果多個線程通路同一資源,我們需要保證通路的時候不會産生沖突,資料修改不會發生錯誤,這就是我們常說的 線程安全 。

那什麼情況下,通路資料時是安全的?什麼情況下,通路資料是不安全的?如何知道你的代碼是否線程安全?要如何通路資料才能保證資料的安全?

本篇文章會一一回答你的問題。

1. 線程不安全是怎樣的?

要搞清楚什麼是線程安全,就要先了解線程不安全是什麼樣的。

比如下面這段代碼,開啟兩個線程,對全局變量 number 各自增 10萬次,每次自增 1。

通俗易懂:說說 Python 裡的線程安全、原子操作

正常我們的預期輸出結果,一個線程自增100萬,兩個線程就自增 200 萬嘛,輸出肯定為 2000000 。

可事實卻并不是你想的那樣,不管你運作多少次,每次輸出的結果都會不一樣,而這些輸出結果都有一個特點是,都小于 200 萬。

以下是執行三次的結果

通俗易懂:說說 Python 裡的線程安全、原子操作

這種現象就是線程不安全,究其根因,其實是我們的操作 number += 1 ,不是原子操作,才會導緻的線程不安全。

2. 什麼是原子操作?

原子操作(atomic operation),指不會被線程排程機制打斷的操作,這種操作一旦開始,就一直運作到結束,中間不會切換到其他線程。

它有點類似資料庫中的 事務。

在 Python 的官方文檔上,列出了一些常見原子操作

通俗易懂:說說 Python 裡的線程安全、原子操作

而下面這些就不是原子操作

通俗易懂:說說 Python 裡的線程安全、原子操作

像上面的我使用自增操作 number += 1,其實等價于 number = number + 1,可以看到這種可以拆分成多個步驟(先讀取相加再指派),并不屬于原子操作。

這樣就導緻多個線程同時讀取時,有可能讀取到同一個 number 值,讀取兩次,卻隻加了一次,最終導緻自增的次數小于預期。

當我們還是無法确定我們的代碼是否具有原子性的時候,可以嘗試通過 dis 子產品裡的 dis 函數來檢視

通俗易懂:說說 Python 裡的線程安全、原子操作

當我們執行這段代碼時,可以看到 number += 1 這一行代碼,由兩條位元組碼實作。

  • BINARY_ADD :将兩個值相加
  • STORE_GLOBAL: 将相加後的值重新指派

每一條位元組碼指令都是一個整體,無法分割,他實作的效果也就是我們所說的原子操作。

當一行代碼被分成多條位元組碼指令的時候,就代表線上程線程切換時,有可能隻執行了一條位元組碼指令,此時若這行代碼裡有被多個線程共享的變量或資源時,并且拆分的多條指令裡有對于這個共享變量的寫操作,就會發生資料的沖突,導緻資料的不準确。

為了對比,我們從上面清單的原子操作拿一個出來也來試試,是不是真如官網所說的原子操作。

這裡我拿字典的 update 操作舉例,代碼和執行過程如下圖

通俗易懂:說說 Python 裡的線程安全、原子操作

從截圖裡可以看到,info.update(new) 雖然也分為好幾個操作

  • LOAD_GLOBAL:加載全局變量
  • LOAD_ATTR: 加載屬性,擷取 update 方法
  • LOAD_FAST:加載 new 變量
  • CALL_FUNCTION:調用函數
  • POP_TOP:執行更新操作

但我們要知道真正會引導資料沖突的,其實不是讀操作,而是寫操作。

上面這麼多位元組碼指令,寫操作都隻有一個(POP_TOP),是以字典的 update 方法是原子操作。

3. 實作人工原子操作

在多線程下,我們并不能保證我們的代碼都具有原子性,是以如何讓我們的代碼變得具有 “原子性” ,就是一件很重要的事。

方法也很簡單,就是當你在通路一個多線程間共享的資源時,加鎖可以實作類似原子操作的效果,一個代碼要嘛不執行,執行了的話就要執行完畢,才能接受線程的排程。

是以,我們使用加鎖的方法,對例子一進行一些修改,使其具備原子性。

通俗易懂:說說 Python 裡的線程安全、原子操作

此時,不管你執行多少遍,輸出都是 2000000.

4. 為什麼 Queue 是線程安全的?

Python 的 threading 子產品裡的消息通信機制主要有如下三種:

  • Event
  • Condition
  • Queue

使用最多的是 Queue,而我們都知道它是線程安全的。當我們對它進行寫入和提取的操作不會被中斷而導緻錯誤,這也是我們在使用隊列時,不需要額外加鎖的原因。

他是如何做到的呢?

其根本原因就是 Queue 實作了鎖原語,是以他能像第三節那樣實作人工原子操作。

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/live

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-05-14

本文作者:王一白

本文來自:“

掘金

”,了解相關資訊可以關注“掘金”