1. 程式人生 > >Linux 多執行緒同步與互斥

Linux 多執行緒同步與互斥

1.同步

同一個程序中的多個執行緒共享所在程序的記憶體資源,當多個執行緒在同一時刻同時訪問同一種共享資源時,需要相互協調,以避免出現數據的不一致和覆蓋等問題,執行緒之間的協調和通訊的就叫做執行緒的同步問題, 執行緒同步的思路: 讓多個執行緒依次訪問共享資源,而不是並行

我們可以使用訊號量進行同步。如:thread2等待thread1傳送訊號量,才能執行printf語句。
#include <stdio.h>   
#include <semaphore.h>   
#include <pthread.h>   
sem_t sem1, sem2;  
  
void *thread1(void *arg) {  
    for (int i = 0; i < 3; ++i) {
     	sem_post(&sem2);
    } 
}  
  
void *thread2(void *arg) {  
    while(1) {
	sem_wait(&sem2);  
    	printf("world!\n"); 
    }
}  
  
int main() {  
    pthread_t t1, t2;     
    sem_init(&sem1,0,1);  
    sem_init(&sem2,0,0);  
      
    pthread_create(&t1,NULL,thread1,NULL);  
    pthread_create(&t2,NULL,thread2,NULL);  
    pthread_join(t1,NULL);  
    pthread_join(t2,NULL);  
    sem_destroy(&sem1);  
    sem_destroy(&sem2);  
    return 0;         
}  


編譯 :gcc -o sem_tb -pthread sem_tb.cpp

執行:./sem_tb

解釋:sem_wait   sem_post

訊號量的資料型別為結構sem_t,它本質上是一個長整型的數。函式sem_init()用來初始化一個訊號量。它的原型為:  

extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));  

sem為指向訊號量結構的一個指標;pshared不為0時此訊號量在程序間共享,否則只能為當前程序的所有執行緒共享;value給出了訊號量的初始值。  

函式sem_post( sem_t *sem )用來增加訊號量的值。當有執行緒阻塞在這個訊號量上時,呼叫這個函式會使其中的一個執行緒不在阻塞,選擇機制同樣是由執行緒的排程策略決定的。

函式sem_wait( sem_t *sem )被用來阻塞當前執行緒直到訊號量sem的值大於0,解除阻塞後將sem的值減一,表明公共資源經使用後減少。函式sem_trywait ( sem_t *sem )是函式sem_wait()的非阻塞版本,它直接將訊號量sem的值減一。函式sem_destroy(sem_t *sem)用來釋放訊號量sem。 

訊號量用sem_init函式建立的,下面是它的說明:
#include<semaphore.h>

 int sem_init (sem_t *sem, int pshared, unsigned int value);
 這個函式的作用是對由sem指定的訊號量進行初始化,設定好它的共享選項,並指定一個整數型別的初始值。pshared引數控制著訊號量的型別。如果 pshared的值是0,就表示它是當前里程的區域性訊號量;否則,其它程序就能夠共享這個訊號量。我們現在只對不讓程序共享的訊號量感興趣。 (這個引數受版本影響), pshared傳遞一個非零將會使函式呼叫失敗。
  這兩個函式控制著訊號量的值,它們的定義如下所示:

#include <semaphore.h>  
int sem_wait(sem_t * sem);  
int sem_post(sem_t * sem); 
這兩個函式都要用一個由sem_init呼叫初始化的訊號量物件的指標做引數。
 sem_post函式的作用是給訊號量的值加上一個“1”,它是一個“原子操作”---即同時對同一個訊號量做加“1”操作的兩個執行緒是不會衝突的;而同時對同一個檔案進行讀、加和寫操作的兩個程式就有可能會引起衝突。訊號量的值永遠會正確地加一個“2”--因為有兩個執行緒試圖改變它。
 sem_wait函式也是一個原子操作,它的作用是從訊號量的值減去一個“1”,但它永遠會先等待該訊號量為一個非零值才開始做減法。也就是說,如果你對一個值為2的訊號量呼叫sem_wait(),執行緒將會繼續執行,介訊號量的值將減到1。如果對一個值為0的訊號量呼叫sem_wait(),這個函式就會地等待直到有其它執行緒增加了這個值使它不再是0為止。如果有兩個執行緒都在sem_wait()中等待同一個訊號量變成非零值,那麼當它被第三個執行緒增加一個“1”時,等待執行緒中只有一個能夠對訊號量做減法並繼續執行,另一個還將處於等待狀態。
 訊號量這種“只用一個函式就能原子化地測試和設定”的能力下正是它的價值所在。還有另外一個訊號量函式sem_trywait,它是sem_wait的非阻塞搭檔。
 最後一個訊號量函式是sem_destroy。這個函式的作用是在我們用完訊號量對它進行清理。下面的定義:
 #include<semaphore.h>
 int sem_destroy (sem_t *sem);
 這個函式也使用一個訊號量指標做引數,歸還自己戰勝的一切資源。在清理訊號量的時候如果還有執行緒在等待它,使用者就會收到一個錯誤。
 與其它的函式一樣,這些函式在成功時都返回“0”。

2.互斥量Mutex:

  • 本質上說就是一把鎖,提供對資源的獨佔訪問,所以Mutex主要的作用是用於互斥。互斥量使同時只能有一個程序訪問資料,可以看做一種的0/1訊號量
  • Mutex物件的值只有0和1。分別代表了Mutex的鎖定狀態和空閒狀態:
    • 鎖定狀態:當前物件被鎖定,使用者程序/執行緒如果試圖Lock臨界資源,則進入等待;
    • 空閒狀態:當前物件為空閒,使用者程序/執行緒可以Lock臨界資源,之後Mutex值減1變為0。
  • Mutex被建立時可以有初始值,表示Mutex被建立後,是鎖定狀態還是空閒狀態。
  • 在同一個執行緒中,為了防止死鎖,系統不允許連續兩次對Mutex加鎖(系統一般會在第二次呼叫立刻返回)。也就是說,加鎖和解鎖這兩個對應的操作,需要在同一個執行緒中完成。
例項中兩個執行緒都要列印printf,但是每次只能有一個執行,另一個需要等待。
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t Device_mutex ;
int count=0;
void *thread_func1(void *args)
{
 while(1)
 {
     pthread_mutex_lock(&Device_mutex);
     printf("thread1: %d\n", count);
     pthread_mutex_unlock(&Device_mutex);
     count++;
     sleep(1);
 }
}
void *thread_func2(void *args)
{
 while(1)
 {
     pthread_mutex_lock(&Device_mutex);
     printf("thread2: %d\n", count);
     pthread_mutex_unlock(&Device_mutex);
     count++;
     sleep(1);
 }
}
int main() {
    int a;
    pthread_t thread1, thread2;
    pthread_mutex_init(&Device_mutex, NULL);
    pthread_create(&thread1, NULL, thread_func1, &a);
    pthread_create(&thread2, NULL, thread_func2, &a);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_mutex_destroy(&Device_mutex);
    return 0;
}


編譯 :gcc -o mutex_hc -pthread mutex_hc.cpp

執行:./mutex_hc

互斥量模型

#include <pthread.h>
pthread_t  mutex                //定義互斥鎖             
pthread_mutex_init()            //初始化鎖          
pthread_mutex_lock()/pthread_mutex_trylock()  ...     //加鎖                  
pthread_mutex_unlock()          //解鎖                        
pthread_mutex_destroy()         //銷燬                        
//成功返回0,失敗返回error number
#include <pthread.h>
int pthread_mutex_init  (pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
int pthread_mutex_lock  (pthread_mutex_t *mutex);
int pthread_mutex_trylock   (pthread_mutex_t *mutex);
int pthread_mutex_unlock    (pthread_mutex_t *mutex);
int pthread_mutex_destroy   (pthread_mutex_t *mutex);

死鎖deadlock

死鎖主要發生在有多個依賴鎖存在時,會在一個執行緒試圖與另一個執行緒沿著相反的順序鎖住互斥量時發生
當程序按照A->的方向使用共享資源,同時白球執行緒按照B->A的順序使用共享資源,不巧的是,程序鎖定A資源直到獲得了釋放了的B資源,白球程序鎖定B資源直到獲得了釋放了的A資源,最後的結果就是他們都不能獲得自己想要的資源,都鎖定著對方想要的資源

解決死鎖:

  • 對共享資源操作前一定要獲得鎖
  • 完成操作後一定要釋放鎖
  • 儘量短的時間佔用鎖
  • 如果有多鎖,如獲得的順序是ABC順序,那麼釋放順序也該是ABC
  • 執行緒錯誤返回時會釋放它所獲得的鎖

互斥VS同步

  • 互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。如果操作是原子操作,那麼天然的具有互斥
  • 同步:是指在互斥的基礎上(大多數情況),通過其它機制實現訪問者對資源的有序訪問。在大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個訪問者同時訪問資源

相關推薦

Linux 執行同步互斥

1.同步 同一個程序中的多個執行緒共享所在程序的記憶體資源,當多個執行緒在同一時刻同時訪問同一種共享資源時,需要相互協調,以避免出現數據的不一致和覆蓋等問題,執行緒之間的協調和通訊的就叫做執行緒的同步問題, 執行緒同步的思路: 讓多個執行緒依次訪問共享資源,而不是並行 我

執行同步互斥

多執行緒執行緒基礎操作 關於本篇部落格的更多程式碼:GitHub連結 執行緒的同步與互斥,學習生產者消費者模型及應用場景 執行緒安全:生產者與消費者模型,讀寫者模型,同步與互斥的實現,互斥鎖,條件變數,posix訊號量,讀寫鎖,自旋鎖 大部分情況,執行緒使用的資料都是區域性變

執行同步互斥(3)

在進行多執行緒程式設計時,難免還要碰到兩個問題,那就執行緒間的互斥與同步: 執行緒同步是指執行緒之間所具有的一種制約關係,一個執行緒的執行依賴另一個執行緒的訊息,當它沒有得到另一個執行緒的訊息時應等待,直到訊息到達時才被喚醒。 執行緒互斥是指對於共享的程序系統資源,在各單個執行緒訪問時的排它性。當有若干個執行

【VS2010】C++執行同步互斥簡單運用

繼以往的想法,寫這點文字,貼上點程式碼,是為了增加自己的記憶,也希望能幫助到需要幫助的人。 1.  互斥量,Mutex #include <Windows.h> #include <iostream> usingnamespace

Linux執行同步機制

一、互斥鎖 儘管在Posix Thread中同樣可以使用IPC的訊號量機制來實現互斥鎖mutex功能,但顯然semphore的功能過於強大了,在Posix Thread中定義了另外一套專門用於執行緒同步的mutex函式。 1. 建立和銷燬    有兩種方法建

Windows執行(四)執行同步互斥問題

執行緒同步與互斥的測試函式如下所示: #include <stdio.h> #include <process.h> #include <Windows.h> #define THREAD_NUM 10 unsigned long g_nNum

Linux 執行同步之哲學家用餐問題

問題描述: 有五個哲學家公用一張餐桌,分別坐在周圍的五張椅子上,在餐桌上有五個碗和五隻筷子,他們的生活方式是交替地進行思考和用餐。平時,一個哲學家進行思考,飢餓時便試圖拿取其左右最靠近他的筷子,只有在他拿到兩隻筷子時才能進餐,進餐完畢,放下筷子繼續思考。(計算機作業系統 第

Linux 執行同步問題 條件變數

互斥鎖:用來上鎖。 條件變數:用來等待,當條件變數用來自動阻塞一個執行緒,直到某特殊情況發生為止。通常條件變數和互斥鎖同時使用。 函式介紹: 1. 名稱: pthread_cond_init 目標: 條件變數初始化 標頭檔案: #include &

Linux執行同步機制 .linux執行程式設計機制

#include <stdio.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex; pthread_cond_t  cond; void * child1(void *arg) {      

Java執行--同步死鎖:synchronized;等待喚醒:wait、notify、notifyAll;生命週期

class Info{ // 定義資訊類 private String name = "李興華"; // 定義name屬性 private String content = "JAVA講師" ; // 定義content屬性 private boolean flag = false ; // 設

執行同步互斥有哪幾種實現方法?

執行緒間的同步方法大體可分為兩類:使用者模式和核心模式。顧名思義,核心模式就是指利用系統核心物件的單一性來進行同步,使用時需要切換核心態與使用者態,而使用者模式就是不需要切換到核心態,只在使用者態完成操作。使用者模式下的方法有:原子操作(例如一個單一的全域性變數),臨界區。核

[Linux]執行同步之sem_wait()學習筆記

2、用Condition Variable實現Semaphore ,即用pthread_cond_wait()等方式實現sem_wait()等方式。 #include <stdlib.h> #include <pthread.h> #include <stdio.h>

Linux 執行同步之訊息佇列

訊息佇列是訊息的連結串列,存放在核心中並有訊息佇列標示符標示。   msgget用於建立一個新佇列或開啟一個現存的佇列。msgsnd將新訊息加入到訊息佇列中;每個訊息包括一個long型的type;和訊息快取;msgrcv用於從佇列中取出訊息;取訊息很智慧,不一定先進先出   ①msgget,建立一個新

[C#學習筆記之執行2]執行同步併發訪問共享資源工具—Lock、Monitor、Mutex、Semaphore

“執行緒同步”的含義         當一個程序啟動了多個執行緒時,如果需要控制這些執行緒的推進順序(比如A執行緒必須等待B和C執行緒執行完畢之後才能繼續執行),則稱這些執行緒需要進行“執行緒同步(thread synchronization)”。         執行緒

linux執行程式設計之互斥

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <unistd.h> #include <pthread.h>

執行同步互斥:讀寫鎖

讀寫鎖基本原理 當有一個執行緒已經持有互斥鎖時,互斥鎖將所有試圖進入臨界區的執行緒都阻塞住。但是考慮一種情形,當前持有互斥鎖的執行緒只是要讀訪問共享資源,而同時有其它幾個執行緒也想讀取這個共享資源,但是由於互斥鎖的排它性,所有其它執行緒都無法獲取鎖,也就無法讀訪問共享資源

linux執行程式設計——同步互斥

我們在前面文章中已經分析了多執行緒VS多程序,也分析了執行緒的使用,現在我們來講解一下linux多執行緒程式設計之同步與互斥。 現在,我們不管究竟是多執行緒好還是多程序好,先講解一下,為什麼要使用多執行緒? 一、 為什麼要用多執行緒技術? 1、避免阻塞,大家知道,單個程序只

Linux執行學習(2)--執行同步互斥及死鎖問題(互斥量和條件變數)

Linux多執行緒學習總結 一.互斥量 1.名詞理解 2.什麼是互斥量(mutex) 3.互斥量的介面 3.1 初始化訊號量 3.2 銷燬訊號量 3.3 互斥量加鎖和解鎖

linux執行程式設計 同步互斥

——本文一個例子展開,介紹Linux下面執行緒的操作、多執行緒的同步和互斥。 前言 執行緒?為什麼有了程序還需要執行緒呢,他們有什麼區別?使用執行緒有什麼優勢呢?還有多執行緒程式設計的一些細節問題,如執行緒之間怎樣同步、互斥,這些東西將在本文中介紹。我在某QQ群裡見到這樣一道面試題: 是否熟悉POSIX多

Linux執行同步互斥的區別

同步與互斥這兩個概念經常被混淆,所以在這裡說一下它們的區別。 一、同步與互斥的區別 1. 同步 同步,又稱直接制約關係,是指多個執行緒(或程序)為了合作完成任務,必須嚴格按照規定的 某種先後次序來執行。 例如,執行緒 T2 中的語句 y 要使用執行緒