十一 執行緒同步

2021-08-19 18:25:30 字數 2644 閱讀 6184

前面完成了多執行緒之後,那麼肯定會涉及到執行緒的同步問題。因為執行緒的執行是隨機的,亂序的。雖然我們這個小kernel實現的排程器演算法比較簡陋,它的隨機性沒那麼強,但是每次進行執行緒切換的時候,還是有可能產生問題。並且問題已經產生了。

int main(void)

return0;}

void k_thread(void *arg)

}

產生這總結果的原因是由上面這段**所致。

我們預期的輸出結果應該是這樣的

可以看到,上面的輸出結果顯得比較雜亂,主要是字串的交界處,會出現卻字元,多空格的現象。而且還引發了gp異常。

這個結果正是證實了之前所說的,每發生一次執行緒切換,都有可能帶來問題。

而帶來問題的原因就是這個裡面的put_str函式。

這個函式主要做了三個事情實現列印

獲取游標值

將游標值轉換為視訊記憶體位址,在此位址處寫入字元

更新游標的值

很明顯這三步應該同時完成,不可拆分的。那麼想象一下這種情況,執行緒1執行完了第一步之後,時間片用完,就被換下了cpu。現在輪到執行緒2執行,執行緒2又會執行第一步,且這個第一步獲取到的游標值會和之前執行緒1獲取到的一樣。然後執行緒2開始向後列印。

終於又輪到執行緒1執行了,當時他是執行到第一步,那麼接下來他就開始執行第二步。可以看到,問題就已經產生了。執行緒1獲取到的游標值早就被執行緒2用到了,那麼執行緒1在列印的過程中就會覆蓋執行緒2列印的資料。

這個是輸出的混亂問題,引發gp異常的原因主要是由於這個簡陋的滾屏操作所致,在滾屏的過程中產生了執行緒切換,導致最後獲取到的視訊記憶體位址會超過視訊記憶體段而引發的gp異常

既然要進行執行緒同步,那麼肯定要在需要同步的地方阻止執行緒的切換。這裡主要通過訊號量的機制對公共資源加鎖,達到同步的目的。

訊號量的原理本省比較簡單。通過p、v操作來表示訊號量的增減。

p、v操作的執行過程

p操作,減少訊號量

1. 判斷訊號量是否大於0

2. 如果大於0, 將其減一

3. 如果小於0,將當前執行緒阻塞

v操作,增加訊號量

1. 將訊號量的值加一

2. 喚醒等待的執行緒

訊號量是乙個全域性的共享資源,所以對其進行增減操作的時候必須是原子操作,這個原子操作通過關中斷來實現。

將執行緒阻塞的操作也很容易實現,只需要將其從就緒佇列中移除即可,下面簡單的看一下實現過程

/* 訊號量結構 */

struct semaphore

;/* 鎖結構 */

struct lock

;

p操作

/* 訊號量down操作 */

void sema_down(struct semaphore *psema)

/* 若訊號量的值等於0,則當前執行緒把自己加入該鎖的等待佇列,然後阻塞自己 */

thread_block(task_blocked); // 阻塞執行緒,直到被喚醒

}/* 若value為1或被喚醒後,會執行下面的**,也就是獲得了鎖。*/

psema->value--;

assert(psema->value == 0);

/* 恢復之前的中斷狀態 */

intr_set_status(old_status);

}

v操作

/* 訊號量的up操作 */

void sema_up(struct semaphore *psema)

psema->value++;

assert(psema->value == 1);

/* 恢復之前的中斷狀態 */

intr_set_status(old_status);

}

獲取鎖和釋放鎖

/* 獲取鎖plock */

void lock_acquire(struct lock *plock)

else

}/* 釋放鎖plock */

void lock_release(struct lock *plock)

assert(plock->holder_repeat_nr == 1);

plock->holder = null; // 把鎖的持有者置空放在v操作之前

plock->holder_repeat_nr = 0;

sema_up(&plock->semaphore); // 訊號量的v操作,也是原子操作

}

阻塞執行緒

/* 當前執行緒將自己阻塞,標誌其狀態為stat. */

void thread_block(task_status stat)

喚醒執行緒

/* 將執行緒pthread解除阻塞 */

void thread_unblock(task_struct* pthread)

list_push(&thread_ready_list, &pthread->general_tag); // 放到佇列的最前面,使其盡快得到排程

pthread->status = task_ready;

}

intr_set_status(old_status);

}

多執行緒(十一)執行緒安全介紹

當多執行緒同時共享,同乙個全域性變數或靜態變數,做寫的操作時,可能會發生資料資料衝突問題。但是做讀的操作時是不會發生資料衝突問題 需求如下 100張車票 有兩個視窗同時搶火車票,請使用多執行緒模擬搶票效果。package com.zuojie author zuojie public class t...

執行緒(一)執行緒的鎖與同步

1.如何避免死鎖 當兩個執行緒需要兩個互斥量,如執行緒一鎖住互斥量a需要互斥量b,但執行緒二鎖住互斥量b需要互斥量a,那麼就會發生死鎖。解決方法1 控制互斥量的枷鎖順序,即不上上述情況發生。2 如果程式複雜,那麼可以嘗試先施放當前鎖,過一段時間在試試。可用pthread mutex trylock函...

Linux學習(二十一) 執行緒

每個程序都有自己單獨的位址空間,這樣在程序切換的時候開銷就會比較大,為了提高系統的效能,許多作業系統規範裡引入了輕量級程序的概念,也被稱為執行緒。在同乙個程序中建立的執行緒共享該程序的位址空間。函式原型 int pthread create pthread t thread,const pthrea...