與鎖有關(guān)的操作
Acquire()
:在進(jìn)入臨界區(qū)前調(diào)用-
Release()
:在離開臨界區(qū)后調(diào)用有的操作系統(tǒng)可能會(huì)用```Lock()/Unlock()```來代替
在
Acquire()
和Release()
之間垒玲,線程將一直占有這個(gè)鎖修然。只有當(dāng)占有鎖的線程釋放鎖的時(shí)候,線程才能獲得鎖二庵。Acquire()
和Release()
一定是成對(duì)出現(xiàn)的。
一個(gè)簡(jiǎn)單的鎖的使用
應(yīng)用在銀行取款:
withdraw(account, amount) {
acquire(lock);
balance = get_balance(account);
balance = balance - amount;
put_balance(account, balance);
release(lock);
return balance;
}
鎖可以是自旋的(a spinlock)佛嬉,也可以是阻塞的(a mutex)
那如何實(shí)現(xiàn)自旋鎖呢镰惦?
- 實(shí)現(xiàn)一:
struct lock{
int held = 0;
}
void acquire(lock) {
while(lock->held);
lock->held = 1;
}
void release(lock){
lock->held = 0;
}
之所以被稱為自旋鎖,是因?yàn)榫€程在鎖沒有被釋放的時(shí)候并不會(huì)進(jìn)入睡眠狀態(tài)镰吵,而是不停地檢查鎖是否被釋放了檩禾。
但這樣的實(shí)現(xiàn)是存在問題的。因?yàn)閮蓚€(gè)自旋中的線程可能同時(shí)發(fā)現(xiàn)held變成了0疤祭,而跳出循環(huán)盼产。
- 實(shí)現(xiàn)一給我們的啟示:
- 在實(shí)現(xiàn)一中
lock->held
其實(shí)是一個(gè)新的共享變量,會(huì)帶來新的臨界區(qū)勺馆。這樣無限循環(huán)戏售,無法解決問題侨核。 -
Acquire()
和Release()
操作必須是原子的。
那么又該如何實(shí)現(xiàn)原子操作呢蜈项?
我們需要硬件的支持:
- 一些原子的指令(如:test-and-set指令)
- test-and-set的語(yǔ)義:
①記錄舊值
②將值設(shè)置為TRUE
③返回舊值
- 對(duì)應(yīng)的代碼(硬件原子地完成如下的工作):
bool test_and_set (bool *flag) {
bool old = *flag;
*flag = TRUE;
return old;
}
- 這是由硬件自動(dòng)實(shí)現(xiàn)的
- Swap/SCHG指令
void Swap (char* x,* y) { // All done atomically char temp = *x; *x = *y; *y = temp }
- 使用Swap實(shí)現(xiàn)test-and-set
bool test_and_set (bool *flag) { bool X = TRUE; Swap(&X, &flag); return X; }
- 開關(guān)中斷
- 對(duì)應(yīng)代碼:
```
structlock { };
void acquire (lock) {
disable interrupts;
}
void release (lock) {
enable interrupts;
}
```
- 開關(guān)中斷在實(shí)際系統(tǒng)中是不具有可行性的芹关。因?yàn)樵趯?shí)際系統(tǒng)中,只有kernel才有資格開關(guān)中斷紧卒,而且隨意開關(guān)中斷也會(huì)導(dǎo)致很多嚴(yán)重的后果侥衬。所以只有在很高要求的同步才會(huì)使用中斷。
- 在有多處理器時(shí)跑芳,關(guān)中斷也是不足夠的
---
**根據(jù)上述實(shí)現(xiàn)的自旋鎖**
根據(jù)test-and-set指令:
struct lock {
int held = 0;
}
void acquire(lock) {
while(test_and_set(&lock->held);
}
void release(lock) {
lock->held = 0;
}
- 只有當(dāng)一開始```lock->held == 0```的時(shí)候才不會(huì)進(jìn)入忙等
- 這在多處理器上也是可行的轴总!
**因?yàn)橥瑫r(shí)只有一個(gè)CPU可以占有內(nèi)存總線,只有一個(gè)CPU能夠讀到```lock->held == 0```的情況**
---
**自旋鎖存在的問題**
- 使用自旋鎖會(huì)使得線程在占用CPU的時(shí)候博个,只是在不停地忙等而不做任何事情怀樟。所以是開銷很大的。
- 解決方法:
- 沒有鎖就主動(dòng)thread_yield()盆佣,來下CPU
- 沒有鎖就進(jìn)入睡眠狀態(tài)往堡,直到可以的時(shí)候再上CPU
---
**在鎖的使用中可能存在如下問題:**
- 臨界區(qū)可能很長(zhǎng),其他線程可能等待的時(shí)間很長(zhǎng)共耍,而且持有鎖的線程隨時(shí)可能下CPU虑灰。