談談多執行緒下為何需要鎖

2021-10-23 14:14:44 字數 3416 閱讀 4487

什麼是鎖?為什麼存在?

多個執行緒執行的時候,共享了同一塊資源,在訪問這塊資源的時候就稱為臨界資源。為了解決這個問題,我們可以為這塊資源加上一把鎖,只允許乙個執行緒訪問這塊資源。

那今天就講講互斥鎖,自旋鎖。

首先先看看不加鎖的情況下多執行緒共享訪問臨界資源會發生什麼.

**很簡單,開啟10個執行緒,各加上100000,理想結果應該是10 * 100000,好的,來試試吧

```c

#include

#include

#define thread_num 10

int count =0;

void

*proc

(void

*arg)

}int

main()

;for

(i =

0; i < thread_num; i++

)while(1

)return0;

}

執行結果

[guojunfeng@guojunfeng mutex]$ ./mutex 

total 9

total 158333

total 317776

total 476812

total 634098

total 793269

total 943308

total 997398

total 997398

total 997398

total 997398

total 997398

很明顯結果997398跟理想狀態1000000少了一些,丟失的那些值去哪了呢?為什麼會導致這種現象,是誰在作怪?

可能很多同學會說count是臨界資源沒有加鎖造成的,那為什麼沒有加鎖達不到理想值呢?這塊臨界資源到底發生了什麼事?

因為count++翻譯成指令,反彙編看看,其實是有三個指令,讀 加 寫。

[guojunfeng@guojunfeng mutex]$ objdump -s -d mutex > count

//找到count++的位置

400642: 8b 05 00 0a 20 00 mov 0x200a00(%rip),%eax # 601048 <__tmc_end__>

400648: 83 c0 01 add $0x1,%eax

40064b: 89 05 f7 09 20 00 mov %eax,0x2009f7(%rip)

# 601048 <__tmc_end__>

大概意思是:

讀取儲存count到臨時變數eax

edx再自增+1

儲存edx到count

本來理想狀態是以下這樣互動達到正確的++

絕大多數的狀態是這樣的,執行緒1完成三個指令後切換執行緒2執行

執行緒1執行緒2

mov 0x200a00(%rip),%eax

add $0x1,%eax

mov %eax,0x2009f7(%rip)

mov 0x200a00(%rip),%eax

add $0x1,%eax

mov %eax,0x2009f7(%rip)

但有少數情況並不是上面這樣的,競爭資源比較大的時候,會變成以下狀態

執行緒1執行緒2

mov 0x200a00(%rip),%eax

mov 0x200a00(%rip),%eax

add $0x1,%eax

mov %eax,0x2009f7(%rip)

add $0x1,%eax

mov %eax,0x2009f7(%rip)

如果此時的count值是10,那執行緒1執行了第一條指令取值10至eax,切換執行緒2進來執行完成三條指令,此時eax是11賦值給了count暫存器。

再次切換回執行緒1,執行緒1的臨時變數eax還是10,再進行自增後11賦值給了count暫存器。

最終這倆個執行緒++後的值是11,並不是12。所以這就可以解釋上面**為什麼沒達到理想值了。

那我們要怎麼做才能讓執行緒1執行++的時候是不讓別的執行緒進來呢?答案就是執行階段加上一把鎖。

互斥鎖和自旋鎖:

#include

#include

#define thread_num 10

int count =0;

pthread_mutex_t mutex;

pthread_spinlock_t spinlock;

void

*proc

(void

*arg)

}int

main()

;pthread_mutex_init

(&mutex,

null);

pthread_spin_init

(&spinlock, pthread_process_shared)

;for

(i =

0; i < thread_num; i++

)while(1

)return0;

}

開啟互斥鎖**就其實加了4行,因為是演示,鎖的銷毀就沒處理,int pthread_mutex_destroy(pthread_mutexattr_t *attr);

定義了乙個mutex的互斥鎖,進行初始化,在進行count++的時候lock,執行完再unlock。

看下執行結果

[guojunfeng@guojunfeng mutex]$ ./mutex 

total 10

total 158645

total 316088

total 475166

total 635056

total 795857

total 955740

total 1000000

total 1000000

total 1000000

total 1000000

其實開啟spinlock開關結果也是正確的。

加鎖的這期間是做了什麼事情呢?

那分別應用在什麼場景下?

mutex是對於處理時間比較長的,比較複雜的

spinlock對於時間較短,比較簡單的

以執行緒切換作為乙個標準,如果這個處理時間超過切換執行緒的代價,就採用mutex,相反採用spinlock。

為何需要建立執行緒

個人以為是為了提高cpu利用率。執行緒給作業系統帶來的建立維護和管理負擔要輕。因為與執行緒相關的資訊比較少,交情的負擔意味著執行緒的代價或開銷比較少。當處理器除乙個程序並啟用另乙個程序時,就要發生上下文切換。為了發生上下文切換,作業系統必須啟動和重新啟動每個程序所需的資訊。這就意味著必須儲存描述程序...

為何需要核函式

生存?還是毀滅?哈姆雷特 可分?還是不可分?支援向量機 之前一直在討論的線性分類器,器如其名 汗,這是什麼說法啊 只能對線性可分的樣本做處理。如果提供的樣本線性不可分,結果很簡單,線性分類器的求解程式會無限迴圈,永遠也解不出來。這必然使得它的適用範圍大大縮小,而它的很多優點我們實在不原意放棄,怎麼辦...

為何需要呼叫「super viewDidLoad」

super didreceivememorywarning void viewdidload 在上面的 中,什麼需要呼叫父類相應的方法呢?以viewdidload為例,父類 super 中的 viewdidload 會幫助你做一些初始化的工作,比如 a是父類,b繼承a,b 在viewdidload ...