信號量基礎入門:并發控制的核心概念

問題的復雜性產生的根本原因在于,如 2.2 節所述,共享變量的訪問始終是“單向信息流”。也就是說,一個進程可以分配新值或檢查當前值,但這種檢查不會為其他進程留下任何痕跡。結果是,當一個進程想要對共享變量的當前值作出反應時,在檢查和隨后執行反應之間,該值可能已被其他進程更改。換句話說,現有的通信機制對于手頭的問題來說是不充分的,我們需要尋找更合適的替代方案。

這種替代方案通過以下方式引入:
a) 在共享變量中引入特殊用途的整數,我們稱之為“信號量(semaphores)”。
b) 在構成各個進程的動作集合中添加兩種新的基本操作,我們分別稱之為“P 操作”和“V 操作”。這些操作始終作用于信號量,并代表并發進程訪問信號量的唯一方式。

信號量本質上是非負整數。如果僅用于解決互斥(Mutual Exclusion)問題,則其值的范圍甚至可以限制為“0”和“1”。荷蘭物理學家兼計算機設計師 C.S. Scholten 博士證明了信號量在取更大值時具有廣泛的應用價值。需要區分時,我們會將它們分別稱為“二進制信號量(binary semaphores)”和“通用信號量(general semaphores)”。我接下來給出的 P 操作和 V 操作的定義不受這種區別的影響。

——Dijkstra 的筆記 EWD 123

引言

事實上,信號量是一個難以理解的概念。它是同步問題的核心,與互斥鎖一起是最先學習的概念之一,但初次接觸時往往難以理解。

通常,信號量可以用以下方式總結:

“信號量是一種通過 P 和 V 這兩個特殊的原子操作來操作表示資源可用性的計數器,從而解決因共享資源并發訪問而導致同步無法保證的問題的技術。”

但是,共享資源到底是什么?原子操作又是什么?資源的可用性、P 和 V 又是什么?
為了回答這些問題,我寫下了這篇文章。

本文的目標是幫助理解并發編程的基礎以及信號量的概念。

什么是共享資源?

當程序并行執行時,多個線程或進程同時訪問的數據被稱為“共享資源(shared resource)”。

  • 示例:內存中的變量、文件句柄、網絡端口、隊列等。

共享資源具有狀態(state)。如果同時對這個狀態進行訪問和修改,可能會引發意外錯誤。

用打印機比喻來理解共享資源

假設有一臺打印機。想象一下,兩個人同時嘗試使用這臺打印機時會發生什么:

  • 如果 A 正在打印,而 B 在中途開始打印會怎樣?
  • 可能會導致打印中斷、輸出重疊,或者打印出空白頁。

這表明對共享資源的并發訪問會引起沖突。

為什么需要原子操作?

那么,如何解決這個問題呢?
核心在于,即使多個線程同時訪問,也要確保狀態的一致性,即保證“原子性(Atomicity)”。

例如,將變量 x 加 1 的操作 x = x + 1 實際上可以分解為以下三個步驟:

  1. 讀取 x 的當前值。
  2. 將其加 1。
  3. 將結果寫回 x

即使是這種看似簡單的操作,如果有其他進程在中間介入,結果可能會被破壞。例如,兩個線程同時執行 x = x + 1 時,最終結果可能只增加 1 而不是預期的 2,甚至可能出現更少的增量。

這種互相競爭修改值的情況導致了意想不到的狀態,這種情況被稱為競態條件(Race Condition)

即使是“看起來像一行代碼的操作”,實際上也可能分為多個步驟執行,因此需要一種防止中間介入的手段。

這就是原子操作 的作用。原子操作是不可分割的單位操作,在執行過程中不允許任何外部干預。

再次用打印機比喻來理解

  • 當 A 向打印機發送打印請求時,只有在打印完全結束、失敗或中止后,B 的請求才能開始處理。
  • 如果在 A 打印過程中 B 插入并開始打印,可能會導致輸出混合或部分丟失的錯誤。
  • 因此,只有在“A 的打印狀態”明確結束后,才能處理下一個請求(B),從而保證輸出狀態的一致性。

這就是原子性的要求條件。
在打印過程中,打印機的狀態(State)應處于“使用中”的鎖定(lock)狀態,
只有在完全結束后才能進行下一個任務。

為了同時管理程序中的基于狀態的資源 ,需要一個無法被中途打斷的原子控制機制

好了,那么原子性是如何保證的呢?這就是我們今天的主題。

Dijkstra 的提議:什么是信號量?

特殊用途的整數(special-purpose integers)

正如之前打印機的例子所示,我們需要能夠從外部明確控制和監控“正在使用中”的狀態。然而,僅靠普通變量無法安全地管理這種狀態。

原因如下:

  • 任何人都可以讀取和寫入變量。
  • 某個進程讀取的值可能在下一刻被其他進程更改。
  • 換句話說,在讀取值并對其做出反應的時間間隔內,狀態可能會發生變化,而這種變化不會被反映出來。

因此,現有的方式是一種容易中斷的“檢查-決定-執行”流程。
為了克服這種結構性限制,Edsger W. Dijkstra 提出了一個新的解決方案。

引入一種特殊用途的整數作為共享變量,我們稱之為“信號量(semaphores)”。
——Dijkstra, EWD 123

什么是信號量?

信號量(semaphore)不僅僅是一個簡單的整數。
它是一個外部訪問受到控制的、具有特殊用途的狀態值。

其核心在于:“禁止直接訪問,只能通過兩種操作(P/V)間接控制。”

在構成各個進程的動作集合中,添加兩種新的基本操作,我們分別稱之為“P 操作”和“V 操作”。這些操作始終作用于信號量,并代表并發進程訪問信號量的唯一方式。
——Dijkstra, EWD 123

這個特殊的整數只能通過以下兩個操作進行操作:

  • P 操作 (Proberen,意為“測試”)
  • V 操作 (Verhogen,意為“增加”)

這兩個操作遵循以下規則:

操作

含義

效果

P(s)

wait / acquire

如果信號量值為正,則減 1 并通過;如果為 0,則等待。

V(s)

signal / release

將信號量值增加 1。

這些操作始終以原子性 方式執行,即它們不會被任何中斷或干擾打斷。

這正是我們一直在尋找的“防止中途介入的機制”。
這就是信號量。

P/V 操作的定義

一個簡單的實現如下:

// 信號量用整數值 s 表示
P(s): // waitwhile (s <= 0) wait;s = s - 1;V(s): // signals = s + 1; // 在此之后,如果有等待中的進程,需要喚醒它們

現代操作系統中,這一過程通過 futex、spinlock、sleep queue 等方式實現。

需要注意的是,在 P/V 操作偽代碼中,s = s - 1s = s + 1while (s <= 0) wait; 條件檢查部分必須作為不可分割的原子操作 執行。

如果 P(s)while (s <= 0) wait; 部分是通過持續占用 CPU 并反復檢查條件是否滿足的方式(即忙等待(busy-waiting 或 spin-waiting) ),那么這將非常低效地使用 CPU 資源。這是在浪費本可以用于其他有用任務的 CPU 時間。

因此,現代操作系統采用以下機制來更高效地處理這種“等待”過程,這些方法可以看作是現代異步處理的核心技術:
睡眠隊列(Sleep Queue / Wait Queue)、上下文切換(Context Switching)、自旋鎖(Spinlock)、以及 Futex(快速用戶空間互斥鎖)
這些方法的整體理論基礎正是上述簡單的操作。

總結一下,在現代操作系統中,信號量操作可以描述如下:

  • 1. 基本的等待/喚醒: 使用睡眠隊列 高效地掛起和喚醒進程(避免忙等待)。
  • 2. 內部原子性保障: 在 P/V 操作短暫的執行期間,為了安全地修改信號量變量(如 s)或睡眠隊列,內核內部可能會使用自旋鎖 等機制。
  • 3. 性能優化(尤其是 Linux): 利用Futex ,當沒有資源競爭時在用戶空間快速處理,只有在發生競爭時才使用內核的睡眠隊列功能,從而減少系統調用開銷。

二進制信號量 vs 通用信號量

區分

含義

使用場景

二進制信號量

值:0 或 1

等同于互斥鎖(Mutex)

通用信號量

值:0 或更大

有限資源(例如:數據庫連接池)

  • C.S. Scholten 的通用信號量概念擴展。

  • 與互斥鎖的區別: 擁有權限的概念 vs 狀態驅動模型

讓我們回到打印機的例子,使用信號量控制打印機。

在打印機示例中應用二進制信號量的過程如下:

  1. 用一個初始值為 s = 1 的信號量表示打印機的狀態。
  2. 用戶 A 調用 P(s),將 s 減為 0 并開始打印。
  3. 在用戶 A 打印過程中,用戶 B 調用 P(s),但由于 s <= 0,用戶 B 進入等待狀態。
  4. 用戶 A 完成打印后調用 V(s),使 s = 1,用戶 B 隨即可以開始打印。

通過這種方式,信號量將資源的“狀態”抽象為數字,并通過原子操作改變該數值,從而實現對并發的控制。

前面提到的二進制信號量 適用于辦公室只有一臺打印機的情況(互斥訪問)。
s=1 表示“打印機可用”,s=0 表示“打印機正在使用”。

但是,如果辦公室有多個相同性能的打印機(例如:3 臺)該怎么辦呢?
在這種情況下,雖然允許多個人同時使用打印機,但必須確保使用的打印機數量不會超過可用的數量。
這正是**通用信號量(General Semaphore)計數信號量(Counting Semaphore)**發揮作用的時候。

通用信號量 s 是一個非負整數值,用于表示可用資源的數量。

使用通用信號量控制 3 臺打印機的示例
  1. 初始狀態:

    • 辦公室有 3 臺打印機,因此信號量 s 的初始值設置為 3s = 3)。
    • 這表示“當前有 3 臺可用的打印機”。
  2. 用戶 A 請求使用打印機(執行 P(s) 操作):

    • 調用 P(s)
    • 當前 s 的值(3)大于 0,因此將 s 減 1(s = 2)。
    • 用戶 A 分配到一臺打印機并開始打印。(現在可用的打印機數量為 2)
  3. 用戶 B 請求使用打印機(執行 P(s) 操作):

    • 調用 P(s)
    • 當前 s 的值(2)大于 0,因此將 s 減 1(s = 1)。
    • 用戶 B 也分配到一臺打印機并開始打印。(現在可用的打印機數量為 1)
  4. 用戶 C 請求使用打印機(執行 P(s) 操作):

    • 調用 P(s)
    • 當前 s 的值(1)大于 0,因此將 s 減 1(s = 0)。
    • 用戶 C 分配到一臺打印機并開始打印。(現在可用的打印機數量為 0)
  5. 用戶 D 請求使用打印機(執行 P(s) 操作):

    • 調用 P(s)
    • 當前 s 的值(0)小于或等于 0,因此用戶 D 會在 P(s) 操作中的 while (s <= 0) wait; 條件下進入等待狀態 ,直到有打印機可用。
  6. 用戶 A 打印完成并歸還打印機(執行 V(s) 操作):

    • 用戶 A 完成打印后調用 V(s)
    • s 增加 1(s = 1)。(現在有 1 臺打印機可用)
    • V(s) 操作完成后,之前在 P(s) 操作中等待的用戶 D 被喚醒,并重新檢查 s 的值。由于 s 現在為 1,用戶 D 將 s 設置為 0 并開始使用打印機。

通用信號量的核心作用:

如上所述,通用信號量不僅僅是一個簡單的二進制“鎖定/解鎖”機制,它還可以精確管理有限資源池的并發訪問 ,并準確跟蹤可用資源的數量

結語

信號量仍然是內核級同步的核心工具,同時也是基于狀態的訪問模型的典型代表。

信號量看似只是一個簡單的整數,
但它卻是系統中用于準確表示資源狀態
安全控制資源 、以及以可預測的方式共享資源的唯一抽象手段

我們必須牢記,在這個小小的整數背后,
承載著無數進程的秩序、沖突避免和系統穩定性

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80579.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80579.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80579.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

(十九)Java集合框架深度解析:從基礎到高級應用

一、集合框架概述 1.1 什么是集合框架 Java集合框架(Java Collections Framework, JCF)是Java語言中用于表示和操作集合的一套標準化體系結構。它提供了一組接口、實現類和算法&#xff0c;用于存儲和操作對象組&#xff0c;解決了數組在存儲對象時的諸多限制。 集合框架的主…

Blender cycles烘焙貼圖筆記

下載了一些槍模型&#xff0c;一個模型有七八個材質&#xff0c;一個扳機、準星還有單獨的材質&#xff0c;用的貼圖只有一小部分有內容&#xff0c;對Draw Call非常不友好。不得不學一下怎么用Blender減材質。 找到了這個視頻如何在Blender中將多種材料多張貼圖烘焙成一張貼圖…

mysql的高可用

1. 環境準備 2臺MySQL服務器&#xff08;node1: 192.168.1.101&#xff0c;node2: 192.168.1.102&#xff09;2臺HAProxy Keepalived服務器&#xff08;haproxy1: 192.168.1.103&#xff0c;haproxy2: 192.168.1.104&#xff09;虛擬IP&#xff08;VIP: 192.168.1.100&#x…

鴻蒙 系統-安全-程序訪問控制-應用權限管控

Ability Kit 提供了一種允許應用訪問系統資源&#xff08;如&#xff1a;通訊錄等&#xff09;和系統能力&#xff08;如&#xff1a;訪問攝像頭、麥克風等&#xff09;的通用權限訪問方式&#xff0c;來保護系統數據&#xff08;包括用戶個人數據&#xff09;或功能&#xff0…

算法-數對的使用

1、數對可用于數組排序中&#xff0c;并且可記憶化排序前的元素下標 #include<iostream> #include<string> #include<bits/stdc.h> using namespace std; typedef long long ll; const int N 2e5 10; pair<int, int> a[N]; void solve() {ll n;cin …

Linux基礎第四天

系統之間文件共享 想要實現兩個不同的系統之間實現文件共享&#xff0c;最簡單的一種方案就是設置VMware軟件的共享文件夾&#xff0c;利用共享文件夾可以實現linux系統和windows系統之間的文件共享&#xff0c;這樣就可以實現在windows系統上編輯程序&#xff0c;然后在linux系…

Docker 核心原理詳解:Namespaces 與 Cgroups 如何實現資源隔離與限制

#Docker疑難雜癥解決指南# Docker 作為容器化技術的代名詞,徹底改變了軟件的開發、部署和管理方式。它憑借其輕量、快速、一致性強的特性,成為了現代云原生架構的基石。然而,Docker 容器的神奇之處并非“無中生有”,其背后是 Linux 內核的兩大核心技術——Namespaces(命名…

GitHub 趨勢日報 (2025年05月14日)

本日報由 TrendForge 系統生成 https://trendforge.devlive.org/ &#x1f310; 本日報中的項目描述已自動翻譯為中文 &#x1f4c8; 今日整體趨勢 Top 10 排名項目名稱項目描述今日獲星總星數語言1xming521/WeClone&#x1f680;從聊天記錄創造數字分身的一站式解決方案&…

【Go】從0開始學習Go

文章目錄 從0開始學習Go0 與C對比1 代碼框架1.1 helloworld式代碼示例1.2 主體代碼元素&#xff08;核心三部分&#xff09;1.3 其他 2 與C/C區別3 有用的小工具4 注意事項 從0開始學習Go 0 與C對比 特性CGo編譯型語言需要編譯為機器碼直接編譯為二進制可執行文件靜態類型類型…

簡單說一下 Webpack分包

最近在看有關webpack分包的知識&#xff0c;搜索了很多資料&#xff0c;感覺這一塊很是迷惑&#xff0c;網上的資料講的也迷迷糊糊&#xff0c;這里簡單總結分享一下&#xff0c;也當個筆記。 如有錯誤請指出。 為什么需要分包 我們知道&#xff0c;webpack的作用&#xff0c…

使用Python和FastAPI構建網站爬蟲:Oncolo醫療文章抓取實戰

使用Python和FastAPI構建網站爬蟲&#xff1a;Oncolo醫療文章抓取實戰 前言項目概述技術棧代碼分析1. 導入必要的庫2. 初始化FastAPI應用3. 定義請求模型4. 核心爬蟲功能4.1 URL驗證和準備4.2 設置HTTP請求4.3 發送請求和解析HTML4.4 提取文章內容4.5 保存結果和返回數據 5. AP…

YoloV8改進策略:卷積篇|風車卷積|即插即用

文章目錄 論文信息論文翻譯摘要引言相關研究紅外搜索與跟蹤檢測和分割網絡紅外搜索與跟蹤數據集的損失函數紅外搜索與跟蹤數據集方法風車形卷積(PConv)基于尺度的動態損失SIRST - UAVB數據集實驗實驗設置與其他方法的比較多模型上的消融實驗結論致謝代碼改進方法測試結果總結…

【NLP】36. 從指令微調到人類偏好:構建更有用的大語言模型

從指令微調到人類偏好&#xff1a;構建更有用的大語言模型 大語言模型&#xff08;LLMs&#xff09;已經成為現代自然語言處理系統的核心&#xff0c;但單純依賴傳統語言建模目標&#xff0c;往往難以滿足實際應用的“人類意圖”。從 Instruction Tuning&#xff08;指令微調&…

基于Transformers與深度學習的微博評論情感分析及AI自動回復系統

前言 這個項目存在cookie沒有自動更新問題&#xff0c;后續可能會發出來解決教程&#xff0c;還有微博網頁版的話最多看到300條評論&#xff0c;而且回復別人信息的話最多回復15條就要休息5分鐘左右才能評論 1. 項目概述 本項目實現了一個微博評論自動化處理系統&#xff0c…

詳解 Zephyr RTOS:架構、功能與開發指南

目錄 Zephyr RTOS 的核心特性 1. 輕量級和可擴展性 2. 實時性能 3. 多平臺支持 4. 安全性 5. 社區和生態系統 Zephyr 的架構 1. 內核 2. 驅動模型 3. 網絡棧 4. 文件系統 開發環境和工具鏈 安裝和配置 開發流程 1. 應用程序開發 2. 調試和測試 3. 部署 實際應…

人工智能重塑醫療健康:從輔助診斷到個性化治療的全方位變革

人工智能正在以前所未有的速度改變著醫療健康領域&#xff0c;從影像診斷到藥物研發&#xff0c;從醫院管理到遠程醫療&#xff0c;AI 技術已滲透到醫療服務的各個環節。本文將深入探討人工智能如何賦能醫療健康產業&#xff0c;分析其在醫學影像、臨床決策、藥物研發、個性化醫…

Linux筆記---內核態與用戶態

用戶態&#xff08;User Mode&#xff09; 權限級別&#xff1a;較低&#xff0c;限制應用程序直接訪問硬件或關鍵系統資源。 適用場景&#xff1a;普通應用程序的運行環境。 限制&#xff1a;無法執行特權指令&#xff08;如操作I/O端口、修改內存管理單元配置等&#xff09…

Spring 代理與 Redis 分布式鎖沖突:一次鎖釋放異常的分析與解決

Spring 代理與 Redis 分布式鎖沖突&#xff1a;一次鎖釋放異常的分析與解決 Spring 代理與 Redis 分布式鎖沖突&#xff1a;一次鎖釋放異常的分析與解決1. 問題現象與初步分析2 . 原因探究&#xff1a;代理機制對分布式鎖生命周期的干擾3. 問題復現偽代碼4. 解決方案&#xff1…

SQL:多列匹配(Multiple-column Matching)

目錄 基礎概念 應用場景詳解 1. 多列等值匹配 2. 多列 IN 匹配&#xff08;集合匹配&#xff09; 3. 多列 JOIN 匹配&#xff08;復合鍵連接&#xff09; 4. 多列匹配 子查詢 5. 多列匹配 EXISTS 6. 多列匹配 UNION&#xff08;組合數據源&#xff09; 7. 多列匹配…

基于DeepSeek的智能客服系統實踐與創新

引言:AI大模型重塑客戶服務新范式 近年來,AI大模型技術的突破性進展正在深刻改變傳統客戶服務模式。作為國內領先的AI企業,DeepSeek憑借其創新的算法架構(如MoE混合專家模型、動態學習率調度器)和極致的成本效益(僅為同類模型成本的1/20),在自然語言理解、情感分析、多…