iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
IT Operation

解構作業系統:30 天從 Process、Concurrency 到 Virtual Memory系列 第 11 篇

Day 11|Semaphore:如果資源不只一份,要怎麼管理?

  • 分享至 

  • xImage
  •  

但現在換一個問題。
假設系統裡不是只有「一份」資源,而是:
Database Connection Pool
Connection 1
Connection 2
Connection 3

我們希望:

  • Thread A → Connection 1
  • Thread B → Connection 2
  • Thread C → Connection 3

三個 Thread 可以同時工作。
Unlocked → 可以進一個
Locked → 其他全部等

就沒有直接表達:
「我有 3 份資源,所以最多允許 3 個使用者同時進來。」的能力。
這時候就需要另一個很經典的 Synchronization Primitive:
Semaphore


1. Semaphore 到底是什麼?它為什麼需要一個 Counter?

Semaphore 最核心的概念,可以先理解成:
由同步機制管理的一個計數狀態,Thread 必須透過特定的原子操作取得或歸還許可。
假設現在有三份相同類型的資源:
Resource A
Resource B
Resource C

並建立Semaphore = 3
這個 3 可以理解成:目前有 3 個 permit(許可)可以被取得。

Thread A 想使用資源:

Semaphore = 3
      │
Thread A 取得一個 permit
      ▼
Semaphore = 2

Thread B:
Semaphore = 2
      │
Thread B 取得一個
      ▼
Semaphore = 1

Thread C:
Semaphore = 1
      │
Thread C 取得一個
      ▼
Semaphore = 0

Thread D
   │
   │ 想取得 permit
   ▼
Semaphore = 0
   │
   ▼
目前沒有可取得的 permit
   │
   ▼
等待

等 Thread B 做完工作,把 permit 歸還:
Thread B 完成
│
│ 歸還
▼
有一個 permit 再次可用

D 才有機會繼續。

所以 Semaphore 和普通整數:
int count = 3;

完全不是同一回事。
Thread A:
讀到 count = 1
→ 可以

Thread B 同時:
讀到 count = 1
→ 我也可以

最後兩邊都以為自己成功取得最後一個資源。

所以 Semaphore 不能只是「一個會加減的變數」。
檢查狀態、取得 permit,以及必要時進入等待,都必須由同步機制正確地協調。

這也是為什麼 Semaphore 是 Synchronization Primitive,而不是我們自己宣告一個 int 就完成了。


2. wait() 和 post() 到底在做什麼?

Semaphore 最常看到兩個操作。
不同 API 名稱會不太一樣,例如 POSIX semaphore 常看到:
sem_wait(&sem);
sem_post(&sem);

在教科書裡也常看到:
wait()
signal()

(1)wait():我要取得一個 permit
Thread A:sem_wait(&sem);
如果目前有可取得的 permit:
3
↓
2
A 成功繼續執行。
Thread B:
2
↓
1
Thread C:
1
↓
0
但是 D:此時已經:Semaphore = 0 所以 D 不能再直接取得 permit。
等有 permit 可以取得時,它才有機會被喚醒並繼續。

(2)post():我歸還/釋出一個 permit
假設 Thread A 工作完成:這代表它釋出一個 permit。
如果已經有等待者:
Thread D → Waiting
Thread E → Waiting

post() 則可以使其中的等待者有機會繼續。
Semaphore 的狀態改變後,等待者可以依實作與排程規則被喚醒並重新參與執行,真正什麼時候在 CPU 上 Running,仍然涉及 Scheduler。

(3)為什麼 wait() 一定要具有 Atomicity?
「檢查還有沒有 permit」和「取得 permit」不能是兩個可以被其他競爭者隨便插入的操作。
也就是:Check + Decrement / Acquire
必須被同步機制安全地完成。


3. Counting Semaphore 到底怎麼管理多份資源?

Counting Semaphore
也就是 Semaphore 的 permit 數量可以大於 1。
Semaphore 管的是「許可」,不一定就是實體資源本身
假設:Semaphore = 3
不代表 Semaphore 裡面真的裝著:
Database Connection 1
Database Connection 2
Database Connection 3

Semaphore 本身主要是在控制:最多允許 3 個 Thread 同時取得/使用這類有限資源,現在允許多少個執行者通過這個同步點。它比較像資源入口的「名額控制」,它只負責:現在還能不能再允許一個使用者進來。


4. Binary Semaphore 和 Mutex 看起來一樣?

Semaphore 初始值設成:Semaphore = 1
Thread A:wait() → 1 → 0
A 可以進入。
Thread B:wait() → 目前沒有 permit → 等待

A:post() 之後 B 才有機會繼續。這種只有:0 或 1
兩種 permit 狀態的 Semaphore,常稱為:Binary Semaphore

(1)ㄟ可是這不就是 Mutex 嗎?

Mutex
→ 同一時間一個持有者

Binary Semaphore
→ 同一時間最多一個 permit
  • Mutex 強調 Ownership,最重要的一個概念是:Thread A,lock(mutex)
    成功後:Mutex Owner = Thread A
    而取得 Mutex 的 Thread,負責釋放這把 Mutex,也就是:
    Thread A lock
    ↓
    Critical Section
    ↓
    unlock

  • Semaphore 則是 Permit

wait()
→ 消耗/取得一個 permit

post()
→ 增加/釋出一個 permit

它通常沒有 Mutex 那種「這把鎖屬於某個 Thread」的 ownership 語意。


5. Semaphore 不只是控制資源,它也能控制當下的執行順序

它能表示:
某件事情發生了幾次/是否已經允許另一個執行流程繼續。
B 一定要等 A 把資料準備好,才能開始,如果沒有 Synchronization:我們不能靠:sleep(1);然後祈禱 A 一秒內一定完成。可以建立:
Semaphore = 0
為什麼是 0?因為一開始:「資料完成」這個 permit 還不存在
Thread B:wait(sem)
因為值是 0 ; B → Waiting

Thread A:
讀取資料
↓
處理完成
↓
post(sem)

現在 B 才能繼續。

在建立一種: Synchronization Relationship。


6. Semaphore 用錯,一樣會把程式鎖死

如果 Thread 成功 wait(),卻忘了:sem_post(&sem);會發生什麼?
假設:Semaphore = 1
Thread A:wait()
↓
1 → 0

然後 A 做完直接離開:忘記 post()
此時,Semaphore = 0,Thread B / C:
wait()
↓
等待

如果之後再也沒有人 post(),大家可能永遠沒有 permit 可以取得。


今天的結論

  • Mutex 最適合表達的概念是:這個 Critical Section同一時間只能有一個 Owner
  • Semaphore 則把概念擴充成:目前有 N 個 Permit
    而它的兩個核心操作:
wait()
→ 嘗試取得一個 permit
→ 沒有時可能 Block

post()
→ 釋出/增加一個 permit
→ 可能讓等待者有機會繼續

當 Mutex 拿不到時,我們說 Thread 可以:Blocked / Sleeping
可是有另一種 Lock 卻選擇:不睡。我就在 CPU 上一直等。
下一篇:

Day 12|Spinlock:為什麼明知道浪費 CPU,Kernel 還是要一直 Spin?


上一篇
Day 10|Mutex:一把鎖到底鎖住了什麼?
下一篇
Day 12|Spinlock:為什麼明知道浪費 CPU,Kernel 還是要一直 Spin?
系列文
解構作業系統:30 天從 Process、Concurrency 到 Virtual Memory 共 16 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言