我們都知道每個線程都有自己獨立的棧空間,自己使用的變量地址也在棧空間內。但是有時候,線程訪問的變量都需要線程共享,這些變量稱爲共享變量,通過數據共享完成線程之間的交互。
我們在進程中瞭解到,多個進程同時訪問一個共享變量的時候,會引發很多問題。所以進程之間需要訪問共享變量的時候,需要互斥。
而對應的,我們線程訪問共享變量,也需要這樣的互斥操作。
我們從一個賣票系統看看,如果沒有使用互斥相關操作,看看會出現什麼樣的結果。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int ticket = 4; //票的總數
void *rout(void *arg)
{
char *id = (char *)arg;
while(1)
{
if(ticket > 0)
{
usleep(1000);
printf("%s sell ticket:%d \n",id,ticket);
ticket--;
}
else
{
break;
}
}
}
int main()
{
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,NULL,rout,"thread1");
pthread_create(&tid2,NULL,rout,"thread2");
pthread_create(&tid3,NULL,rout,"thread3");
pthread_join(tid1,NULL);
pthread_join(tid1,NULL);
pthread_join(tid1,NULL);
return 0;
}
看一下運行結果:
很明顯,運行的結果顯示,不符合我們預期的效果。
我們的票數爲4張,但是可以看到最後thread2 都賣到了 -1 張票了,這是不符合實際的問題的結果。
由於ticket 是全局變量,每個線程都可以訪問到它,每個線程都在可能切換到運行狀態,在線程函數裏,出現了usleep() 等待,這時候系統可能切換到另一個進程中,又賣出了一張票,等該線程的運行時間到了後,再返回原來線程的時候,還繼續對票數減一了。這就導致了程序的不符合實際性。
我們應該怎麼解決這個問題呢?
1.代碼必須要有互斥行爲,當代碼進入臨界區執行時,不允許其他線程進入該臨界區。
2.如果多個線程同時要求執行臨界區的代碼,並且臨界區沒有線程在執行時,那麼只允許一個線程進入該臨界區。
3.如果線程不在臨界區中執行,那麼該線程不能阻止其他線程進入到臨界區。
這就是給臨界區上了一把鎖。 Linux上提供的這邊鎖叫做互斥量。
我們還可以這樣定義互斥量:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER ;
這樣定義的互斥量不需要進行初始化和刪除,系統會自動的初始化和刪除。
互斥量:
1.定義一個互斥量 pthread_mutex_t mutex
2.初始化信號量 pthread_mutex_init(&mutex,NULL); //初始化成1
3.上鎖 pthread_mutex_lock(&mutex);
1->0,返回
0 ,等待
4.銷燬 pthread_mutex_destroy(&mutex);
這樣我們改進一下我們的買票系統:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int ticket = 4;
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex; //定義互斥量
void *rout(void *arg)
{
char *id = (char *)arg;
while(1)
{
pthread_mutex_lock(&mutex); //上鎖
if(ticket > 0)
{
usleep(1000);
printf("%s sell ticket:%d \n",id,ticket);
ticket--;
pthread_mutex_unlock(&mutex); //解鎖
}
else
{
pthread_mutex_unlock(&mutex); //解鎖
break;
}
}
}
int main()
{
pthread_t tid1,tid2,tid3;
pthread_mutex_init(&mutex,NULL); //初始化互斥量
pthread_create(&tid1,NULL,rout,"thread1");
pthread_create(&tid2,NULL,rout,"thread2");
pthread_create(&tid3,NULL,rout,"thread3");
pthread_join(tid1,NULL);
pthread_join(tid1,NULL);
pthread_join(tid1,NULL);
pthread_mutex_destroy(&mutex); //銷燬互斥量
return 0;
}
運行結果圖:
添加了互斥量過後,該系統的買票結果與我們的預想效果一樣。