全域性靜態變數,互斥訊號量等在記憶體中是怎麼處理?

時間 2021-06-01 14:08:50

1樓:

L1和L2 Cache之間有Coherency protocol的。MESI裡面乙個Cache打算改乙個變數的時候會通知別的Cache,並令後者中的變數副本失效。這樣可以保證所有的Cache都能看到同樣的變數值。

針對Mutex這種變數還有額外的硬體實現,來保證在任一時刻最多只有乙個CPU獨佔地對乙個mutex享有寫許可權。這種獨佔的寫許可權只靠上面的Cache coherency是不夠的。同時軟體層面也需要專門的指令(或者原語)來指明mutex的變數操作需要硬體的獨佔功能的輔助。

2樓:墨無知

發出訊號量,給它的值加1,然後喚醒正在等待該訊號量的程序或執行緒

int sem_post(sem_t *sem);

成功返回0;失敗返回-1,不會改變它的值,設定errno,該函式是非同步訊號安全的,可以在訊號處理程式裡呼叫它

無名訊號量,用於程序體內各執行緒間的互斥和同步,使用如下API(無名訊號量,基於記憶體的訊號量)

(1)、sem_init

功能:用於建立乙個訊號量,並初始化訊號量的值。

標頭檔案:

函式原型: int sem_init (sem_t* sem, int pshared, unsigned int value);

函式傳入值: sem:訊號量。pshared:決定訊號量能否在幾個程序間共享。由於目前LINUX還沒有實現程序間共享資訊量,所以這個值只能取0。

(2)其他函式。

int sem_wait (sem_t* sem);

int sem_trywait (sem_t* sem);

int sem_post (sem_t* sem);

int sem_getvalue (sem_t* sem);

int sem_destroy (sem_t* sem);

功能:sem_wait和sem_trywait相當於P操作,它們都能將訊號量的值減一,兩者的區別在於若訊號量的值小於零時,sem_wait將會阻塞程序,而sem_trywait則會立即返回

。sem_post相當於V操作,它將訊號量的值加一,同時發出喚醒的訊號給等待

的程序(或執行緒)。

sem_getvalue 得到訊號量的值。

sem_destroy 摧毀訊號量。

如果某個基於記憶體的訊號燈是在不同程序間同步的,該訊號燈必須存放在共享記憶體區中,這要只要該共享記憶體區存在,該訊號燈就存在。

互斥鎖(又名互斥量)強調的是資源的訪問互斥:互斥鎖是用在多執行緒多工互斥的,乙個執行緒占用了某乙個資源,那麼別的執行緒就無法訪問,直到這個執行緒unlock,其他的

執行緒才開始可以利用這個資源。比如對全域性變數的訪問,有時要加鎖,操作完了,在解鎖。有的時候鎖和訊號量會同時使用的」

也就是說,訊號量不一定是鎖定某乙個資源,而是流程上的概念,比如:有A,B兩個執行緒,B執行緒要等A執行緒完成某一任務以後再進行自己下面的步驟,這個任務並不一定是鎖

定某一資源,還可以是進行一些計算或者資料處理之類。而執行緒互斥量則是「鎖住某一資源」的概念,在鎖定期間內,其他執行緒無法對被保護的資料進行操作。在有些情況

下兩者可以互換。

在linux下, 執行緒的互斥量資料型別是pthread_mutex_t. 在使用前, 要對它進行初始化:

對於靜態分配的互斥量, 可以把它設定為PTHREAD_MUTEX_INITIALIZER, 或者呼叫pthread_mutex_init.

對於動態分配的互斥量, 在申請記憶體(malloc)之後, 通過pthread_mutex_init進行初始化, 並且在釋放記憶體(free)前需要呼叫pthread_mutex_destroy.

原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restric attr);

int pthread_mutex_destroy(pthread_mutex_t *mutex);

標頭檔案:

返回值: 成功則返回0, 出錯則返回錯誤編號.

說明: 如果使用預設的屬性初始化互斥量, 只需把attr設為NULL. 其他值在以後講解.

首先說一下加鎖函式:

標頭檔案:

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);

返回值: 成功則返回0, 出錯則返回錯誤編號.

說明: 具體說一下trylock函式, 這個函式是非阻塞呼叫模式, 也就是說, 如果互斥量沒被鎖住, trylock函式將把互斥量加鎖, 並獲得對共享資源的訪問許可權; 如果互斥量

被鎖住了, trylock函式將不會阻塞等待而直接返回EBUSY, 表示共享資源處於忙狀態.

再說一下解所函式:

標頭檔案:

原型: int pthread_mutex_unlock(pthread_mutex_t *mutex);

返回值: 成功則返回0, 出錯則返回錯誤編號.

條件變數常與互斥鎖同時使用,達到執行緒同步的目的:條件變數通過允許執行緒阻塞和等待另乙個執行緒傳送訊號的方法彌補了互斥鎖的不足。在傳送訊號時,如果沒有執行緒

等待在該條件變數上,那麼訊號將丟失;而訊號量有計數值,每次訊號量post操作都會被記錄

1. 互斥鎖必須是誰上鎖就由誰來解鎖,而訊號量的wait和post操作不必由同乙個執行緒執行。

2. 互斥鎖要麼被鎖住,要麼被解開,和二值訊號量類似

3. sem_post是各種同步技巧中,唯一乙個能在訊號處理程式中安全呼叫的函式

4. 互斥鎖是為上鎖而優化的;條件變數是為等待而優化的; 訊號量既可用於上鎖,也可用於等待,因此會有更多的開銷和更高的複雜性

5. 互斥鎖,條件變數都只用於同乙個程序的各執行緒間,而訊號量(有名訊號量)可用於不同程序間的同步。當訊號量用於程序間同步時,要求訊號量建立在共享記憶體區。

6. 訊號量有計數值,每次訊號量post操作都會被記錄,而條件變數在傳送訊號時,如果沒有執行緒在等待該條件變數,那麼訊號將丟失。

讀寫鎖讀寫鎖與互斥量類似,不過讀寫鎖允許更高的並行性。互斥量要麼是鎖住狀態要麼是不加鎖狀態,而且一次只有乙個執行緒可以對其加鎖。

讀寫鎖可以由三種狀態:讀模式下加鎖狀態、寫模式下加鎖狀態、不加鎖狀態。一次只有乙個執行緒可以占有寫模式的讀寫鎖,但是多個執行緒可以同時占有讀模式的讀寫

鎖。在讀寫鎖是寫加鎖狀態時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的執行緒都會被阻塞。當讀寫鎖在讀加鎖狀態時,所有試圖以讀模式對它進行加鎖的執行緒都可以

得到訪問權,但是如果執行緒希望以寫模式對此鎖進行加鎖,它必須阻塞直到所有的執行緒釋放讀鎖。雖然讀寫鎖的實現各不相同,但當讀寫鎖處於讀模式鎖住狀態時,如果有

另外的執行緒試圖以寫模式加鎖,讀寫鎖通常會阻塞隨後的讀模式鎖請求。這樣可以避免讀模式鎖長期占用,而等待的寫模式鎖請求一直得不到滿足。

讀寫鎖非常適合於對資料結構讀的次數遠大於寫的情況。當讀寫鎖在寫模式下時,它所保護的資料結構就可以被安全地修改,因為當前只有乙個執行緒可以在寫模式下擁

有這個鎖。當讀寫鎖在讀狀態下時,只要執行緒獲取了讀模式下的讀寫鎖,該鎖所保護的資料結構可以被多個獲得讀模式鎖的執行緒讀取。

讀寫鎖也叫做共享-獨佔鎖,當讀寫鎖以讀模式鎖住時,它是以共享模式鎖住的;當他以寫模式鎖住時,它是以獨佔模式鎖住的。

初始化和銷毀:

#include

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

成功則返回0, 出錯則返回錯誤編號.

同互斥量以上, 在釋放讀寫鎖占用的記憶體之前, 需要先通過pthread_rwlock_destroy對讀寫鎖進行清理工作, 釋放由init分配的資源.

讀和寫:

#include

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

成功則返回0, 出錯則返回錯誤編號.

這3個函式分別實現獲取讀鎖, 獲取寫鎖和釋放鎖的操作. 獲取鎖的兩個函式是阻塞操作,

同樣, 非阻塞的函式為:

#include

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

成功則返回0, 出錯則返回錯誤編號.

非阻塞的獲取鎖操作, 如果可以獲取則返回0, 否則返回錯誤的EBUSY.

雖然讀寫鎖提高了並行性,但是就速度而言並不比互斥量快.

可能這也是即使有讀寫鎖存在還會使用互斥量的原因,因為他在速度方面略勝一籌。這就需要我們在寫程式的時候

綜合考慮速度和並行性並找到乙個折中。

比如: 假設使用互斥量需要0.5秒,使用讀寫鎖需要0.8秒。在類似學生管理系統這類軟體中,可能百分之九十的

時間都是查詢操作,那麼假如現在突然來個個20個請求,如果使用的是互斥量,那麼最後的那個查詢請求被滿足需要10後。這樣,估計沒人能受得了。而使用讀寫鎖,應為

讀鎖能夠多次獲得。所以所有的20個請求,每個請求都能在1秒左右

得到滿足。

也就是說,在一些寫操作比較多或是本身需要同步的地方並不多的程式中我們應該使用互斥量,而在讀操作遠大於寫操作的一些程式中我們應該使用讀寫鎖來進行同步

成功則返回0, 出錯則返回錯誤編號.

這兩個函式分別是阻塞等待和超時等待.

等待條件函式等待條件變為真, 傳遞給pthread_cond_wait的互斥量對條件進行保護, 呼叫者把鎖住的互斥量傳遞給函式. 函式把呼叫執行緒放到等待條件的執行緒列表上,

然後對互斥量解鎖, 這兩個操作是原子的. 這樣便關閉了條件檢查和執行緒進入休眠狀態等待條件改變這兩個操作之間的時間通道, 這樣執行緒就不會錯過條件的任何變化.

當pthread_cond_wait返回時, 互斥量再次被鎖住.

pthread_cond_wait函式的返回並不意味著條件的值一定發生了變化,必須重新檢查條件的值。

pthread_cond_wait函式返回時,相應的互斥鎖將被當前執行緒鎖定,即使是函式出錯返回。

阻塞在條件變數上的執行緒被喚醒以後,直到pthread_cond_wait()函式返回之前條件的值都有可能發生變化。所以函式返回以後,在鎖定相應的互斥鎖之前,必須重新測試條

件值。最好的測試方法是迴圈呼叫pthread_cond_wait函式,並把滿足條件的表示式置為迴圈的終止條件。如:

pthread_mutex_lock();

while (condition_is_false)

pthread_cond_wait();

pthread_mutex_unlock();

阻塞在同乙個條件變數上的不同執行緒被釋放的次序是不一定的。

注意:pthread_cond_wait()函式是退出點,如果在呼叫這個函式時,已有乙個掛起的退出請求,且執行緒允許退出,這個執行緒將被終止並開始執行善後處理函式,而這時和條

件變數相關的互斥鎖仍將處在鎖定狀態。

pthread_cond_timedwait函式到了一定的時間,即使條件未發生也會解除阻塞。這個時間由引數abstime指定。函式返回時,相應的互斥鎖往往是鎖定的,即使是函式出錯返

回。注意:pthread_cond_timedwait函式也是退出點。

超時時間引數是指一天中的某個時刻。使用舉例:

pthread_timestruc_t to;

to.tv_sec = time(NULL) + TIMEOUT;

to.tv_nsec = 0;

超時返回的錯誤碼是ETIMEDOUT。

3. 通知條件:

#include

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

成功則返回0, 出錯則返回錯誤編號.

這兩個函式用於通知執行緒條件已經滿足. 呼叫這兩個函式, 也稱向執行緒或條件傳送訊號. 必須注意, 一定要在改變條件狀態以後再給執行緒傳送訊號.

Android 程式設計中,使用靜態變數有哪些缺點?應該如何規範使用?

Stanley Luo 關於Android 使用靜態變數,我遇到過的情況是 當應用不處於與當前使用者互動的情況時 例如回到HOME,跳到其他應用 離開的時間比較長時,應用的靜態變數有可能被置null,是不保險的。 郭勳 我好像使用static比較多的只有兩個地方,乙個是用static final定義...

靜態變數是為了解決什麼問題而存在的?

小小何先生 對這個問題的補充描述如下 它提供了什麼好處?有什麼場景需要使用到它?我在搜尋引擎搜到的都只是說什麼語法方便.還有編譯方面的實現之類的這些內容.但是我關心的是它是為了什麼而誕生的?好!好乙個只關心為了什麼而誕生的!說一下c 中的靜態變數吧!程式在處理的時候,都是處理資料,或者稱之為變數。變...

c 函式區域性靜態變數第二次被訪問的時候具體做了些什麼?

Atlantis 別的不知道用g 測試了一下單例標記 雙檢測鎖,只是加乙個bool標記是否初始化的話,如果在建構函式裡加個sleep,還是還是會出問題.include include using namespace std class A A singleon void do something i...