• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • 【線程同步與互斥】互斥鎖(mutex)

    在多線程訪問共享數據的時候可能會發生沖突,例如:

    /*沖突的例子*/
    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #include<unistd.h>
    
    int common_data=0;//公共數據 
    
    void increase_data(void* vptr){
    	//讓common_data自增10次
    	//兩個線程都來執行它,如果不沖突的話common_data最后會等于20
    	int val; 
    	for(int i=0;i<10;i++){
    	 	val=common_data;
    	 	sleep(vptr);
    	 	printf("線程%d:common_data=%d\n",(unsigned int)pthread_self(),common_data);
    	 	common_data=val+1;
    	}
    	return NULL;
    }
    
    int main(void){
    	pthread_t tid1,tid2;
    	int err;
    	err=pthread_create(&tid1,NULL,(void*)increase_data,(void *)1);
    	if(err!=0){//創建失敗 
    		printf("failed to create thread\n");
    		exit(1);
    	}
    	err=pthread_create(&tid2,NULL,(void*)increase_data,(void *)2);
    	if(err!=0){//創建失敗 
    		printf("failed to create thread\n");
    		exit(1);
    	}
    	pthread_join(tid1,NULL);
    	pthread_join(tid2,NULL);
    	return 0;
    }

    我們會觀察到:


    數據完全亂套了,沒有得到預期的20。

    和這個類似的還有一個有趣的例子:

    /*沒有加鎖但是沒有沖突的情況*/
    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #include<unistd.h>
    
    int common_data=0;//公共數據 
    
    void pthread1(void* arg);//第一個線程 
    void pthread1(void* arg);//第二個線程 
    
    void pthread1(void* arg){
    	//線程1要修改公共的數據common_data
    	//按照預期,執行后common_data=10 
    	for(int i=0;i<10;i++){
    		common_data++;
    		//sleep(2);
    		printf("pthread1:第%d次循環,common_data=%d\n",i,common_data);
    	}
    }
    
    void pthread2(void* arg){
    	//線程2要修改公共的數據common_data
    	//按照預期,執行后common_data=10
    	//如果兩個線程不發生沖突,common_data最后等于20 
    	for(int i=0;i<10;i++){
    		common_data++;
    		//sleep(1);
    		printf("pthread2:第%d次循環,common_data=%d\n",i,common_data);
    	}
    }
    
    int main(void){
    	pthread_t tid1,tid2;
    	int err;
    	err=pthread_create(&tid1,NULL,(void*)pthread1,NULL);
    	if(err!=0){//創建失敗 
    		printf("failed to create thread\n");
    		exit(1);
    	}
    	err=pthread_create(&tid2,NULL,(void*)pthread2,NULL);
    	if(err!=0){//創建失敗 
    		printf("failed to create thread\n");
    		exit(1);
    	}
    	pthread_join(tid1,NULL);
    	pthread_join(tid2,NULL);
    	return 0;
    }

    這個例子運行以后沒有沖突,這是為什么呢?!在和同學討論并經過自己動手實踐以后證實,其實只要把循環次數增加,比如增加到10000000,還是會觀察到沖突的,如下圖:

    也就是說,當數據量小的時候,執行速度較快,給人的錯覺是“順序”執行的,但是數據量一旦大起來,總有某次讀寫操作相互過不去......。然后common_data的值就亂了。這也體現了所謂“量變引起質變”吧。

    為了解決多個線程訪問同一個數據(可能)會出現的沖突問題,一個行之有效的方法就是加互斥鎖(MUTual EXclusive lock,mutex)。相關的函數:

    (1)pthread_mutex_init

    用來申請一個互斥鎖。

    例如:

    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex,NULL);
    第一個參數傳入pthread_mutex_t類型的指針,第二個指定了需要的屬性,一般用NULL。

    (2)pthread_mutex_lock

    給代碼段加鎖。加鎖意味著當前只有這個鎖定這個代碼段的線程能夠執行它,如果其他線程要執行這些代碼,這些線程就會被掛起。

    (3)pthread_mutex_trylock

    功能同(2),但是這個函數是“嘗試加鎖”,也就是說如果加鎖失敗了,函數會立刻返回,線程繼續執行。

    (4)pthread_mutex_unlock

    解鎖(但鎖還在,區別下面的pthread_mutex_destroy),和(3)、(4)配合使用。

    (5)pthread_mutex_destroy

    徹底銷毀鎖,功能同解鎖,如果我們不再需要某個鎖了就可以銷毀它。

    有了互斥鎖的機制,就可以解決剛才的沖突問題了:

    /*沒有加鎖但是沒有沖突的情況*/
    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #include<unistd.h>
    
    int common_data=0;//公共數據 
    pthread_mutex_t mutex;
     
    void pthread1(void* arg);//第一個線程 
    void pthread1(void* arg);//第二個線程 
    
    void pthread1(void* arg){
    	//線程1要修改公共的數據common_data
    	//按照預期,執行后common_data=10 
    	for(int i=0;i<10;i++){
    		pthread_mutex_lock(&mutex);//加鎖 
    		common_data++;
    		printf("pthread1:第%d次循環,common_data=%d\n",i,common_data);
    		pthread_mutex_unlock(&mutex);//解鎖 
    	}
    }
    
    void pthread2(void* arg){
    	//線程2要修改公共的數據common_data
    	//按照預期,執行后common_data=10
    	//如果兩個線程不發生沖突,common_data最后等于20 
    	for(int i=0;i<10;i++){
    		pthread_mutex_lock(&mutex);//加鎖 
    		common_data++;
    		printf("pthread2:第%d次循環,common_data=%d\n",i,common_data);
    		pthread_mutex_unlock(&mutex);//解鎖 
    	}
    }
    
    int main(void){
    	pthread_t tid1,tid2;
    	int err;
    	pthread_mutex_init(&mutex,NULL);//初始化鎖 
    	err=pthread_create(&tid1,NULL,(void*)pthread1,NULL);
    	if(err!=0){//創建失敗 
    		printf("failed to create thread\n");
    		exit(1);
    	}
    	err=pthread_create(&tid2,NULL,(void*)pthread2,NULL);
    	if(err!=0){//創建失敗 
    		printf("failed to create thread\n");
    		exit(1);
    	}
    	pthread_join(tid1,NULL);
    	pthread_join(tid2,NULL);
    	pthread_mutex_destroy(&mutex);//將鎖徹底銷毀 
    	return 0;
    }
    運行效果:


    這一次盡管執行次數很大,但是沒有沖突了,得到了預期的2000000。


    版權聲明:本文為lishichengyan原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/lishichengyan/article/details/78288586

    智能推薦

    線程同步與互斥

    mutex互斥量 大部分情況,線程使用的數據都是局部變量,變量的地址空間在線程棧空間內,這種情況,變量歸屬單個線程。 但有時候很多變量都需要在線程間共享,這樣的變量稱為共享變量,可以通過數據的共享,完成線程之間的交互。 多個線程并發的操作共享變量,會帶來一些問題。 舉個妮子: 測試結果如下: 我們可以看到一個非常明顯的錯誤,當票數已經沒有了的時候,某些線程依然買到了票。那么這又是為什么呢? 1、i...

    線程同步與互斥

    mutex(互斥量) 大部分情況下,線程使用的數據都是局部變量,變量的地址空間在線程的棧空間內。這種情況下,變量歸屬于單個線程,其他線程無法獲得這種變量。 但有時候,很多變量都需要在線程間共享,這樣的變量稱為共享變量,可以通過數據的共享,完成線程之間的交互。 多個線程并發的操作共享變量,會帶來一些問題。 代碼實例: 結果演示: . . . . . .  為什么操作結果并不是我們預想的那樣...

    線程同步之互斥鎖

    目錄 一、概述 產生死鎖的情況: 一些注意事項: 二、程序接口 頭文件: 鏈接庫: 數據結構: 函數清單: 函數詳解: 三、示例程序(僅供參考) 四、參考文獻 一、概述 互斥鎖一般用于線程同步,其主要用于保護臨界區,防止多個線程同時修改某些數據。互斥鎖使用不當會造成死鎖的情況,可能會導致程序卡死,這個需要開發者格外小心。   產生死鎖的情況: 1、一個函數里面對一個互斥鎖加鎖之后,因為某...

    線程同步之互斥鎖:pthread_mutex_init,pthread_mutex_lock,pthread_mutex_unlock,pthread_mutex_destroy

    主要函數說明 int pthread_mutex_init (pthread_mutex_t *__mutex,const pthread_mutexattr_t *__mutexattr)創建一個鎖; int pthread_mutex_destroy (pthread_mutex_t *__mutex)銷毀鎖; int pthread_mutex_trylock (pthread_mutex_...

    GO 互斥鎖(Mutex)原理

    文章目錄 1. 前言 2. Mutex數據結構 2.1 Mutex結構體 2.2 Mutex方法 3. 加解鎖過程 3.1 簡單加鎖 3.2 加鎖被阻塞 3.3 簡單解鎖 3.4 解鎖并喚醒協程 4. 自旋過程 4.1 自旋條件 4.2 自旋的優勢 4.3 自旋的問題 5. Mutex模式 5.1 normal模式 5.2 starvation模式 6. Woken狀態 7. 為什么重復解鎖要pa...

    猜你喜歡

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡