二 Linux--多線程( 二 )


#include <stdio.h>#include <pthread.h>#include <unistd.h>pthread_mutex_t mutex;// 創建鎖變量//全局變量,所有線程共享int ticket = 10;void* get_tickets(void* arg){ long id = (long)arg; while (1){usleep(1000);// 加鎖pthread_mutex_lock(&mutex);if (ticket > 0){// 有票--ticket;printf("線程%ld獲得一張票,剩余%d張票\n",id,ticket);// 解鎖pthread_mutex_unlock(&mutex);}else{// 無票 , 退出// 解鎖pthread_mutex_unlock(&mutex);break;} }}int main(){ pthread_t t[5]; // 初始化鎖 pthread_mutex_init(&mutex, NULL); // 創建5個線程 long i = 0; for (; i < 5; ++i) { pthread_create(t+i, NULL, get_tickets, (void*)(i+1)); } // 釋放5個線程 for (i = 0; i < 5; ++i) {pthread_join(t[i], NULL); } // 銷毀鎖 pthread_mutex_destroy(&mutex); return 0;}運行結果如下:

二 Linux--多線程

文章插圖
總結幾點并回答幾個問題:
鎖的作用: 對臨界區進行保護,所有的執行流線程都必須遵守這個規則:lock——>訪問臨界區——>unlock
需要注意的點:
  • 所有的線程必須看到同一把鎖,鎖本身就是臨界資源,所以鎖本身需要先保證自身安全申請鎖的過程不能出現中間態,必須保證原子性
  • 任一線程持有鎖之后 , 其它線程如果還想申請鎖時申請不到的,保證互斥性
線程申請不到鎖此時會做什么?
進入等待隊列進行等待 , 從運行隊列轉移到等待隊列,狀態由R變成S,持有鎖的線程unlock之后,需要喚醒等待隊列中的第一個線程
struct mutex{  int lock;// 0 1     // ...     sturct wait_queue;//鎖下的等待隊列}互斥量的原理大多數體系結構都提供了swap或exchange指令 , 該指令的作用是把寄存器和內存單元的數據相交換,由于只有一條指令,保證了原子性,即使是多處理器平臺,訪問內存的總線周期也有先后,一個處理器上的交換指令執行時另一個處理器的交換指令只能等待總線周期 。下面是lock和unlock的偽代碼
lock: movb $0, %a1     # 把0值放進寄存器a1里 xchgb %a1, mutex # 交換a1寄存器的內容和鎖的值(無線程使用鎖時 , metux的值為1) if (%a1 > 0)return 0; # 得到鎖 else掛起等待; goto lock;unlock: movb $1 mutex  #把1賦給鎖 喚醒等待的線程; return 0;在上述加鎖的偽代碼中演示了上步驟:
  1. 對寄存器的內容進行清0
  2. 把mutex的值(被使用值為0,未被使用值為1)和寄存器的內容進行交換
  3. 寄存器的內容為1代表得到了鎖,為0代表未得到鎖,要掛起等待

二 Linux--多線程

文章插圖
解鎖的偽代碼步驟(只有有鎖的線程才可以執行到這段代碼):
  1. 把mutex的值改為1
  2. 喚醒等待鎖的線程
死鎖概念: 死鎖是指兩個或兩個以上的進程在執行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去 。此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程 。
舉個例子:
二 Linux--多線程

文章插圖
這里線程1先申請資源1,申請到了之后,資源1被鎖死(資源1會永遠被線程1申請,因為只有申請到資源2執行完臨界代碼,才會釋放掉資源1,此時線程1被卡在申請資源2的點,根本走不到釋放資源1的代碼,所以會一直被線程1占有) , 線程2無法申請,線程2先申請資源2,同樣資源2也被鎖死,這樣當線程1繼續向下申請資源2的時候,就被阻塞在那里,線程2在向下申請資源1的時候,也被阻塞在那里,這就形成了死鎖,永遠解不了鎖 。
死鎖引起的原因: