這篇文章介紹linux下線程同步與互斥機制–互斥鎖,在多線程并發(fā)的時候,都會出現(xiàn)多個消費者取數(shù)據(jù)的情況,這種時候數(shù)據(jù)都需要進行保護,比如: 火車票售票系統(tǒng)、汽車票售票系統(tǒng)一樣,總票數(shù)是固定的,但是購票的終端非常多。
互斥鎖就是用來保護某一個資源不能同時被2個或者2個以上的線程使用。
為什么需要加鎖?
就是因為多個線程共用進程的資源,要訪問的是公共區(qū)間時(全局變量),當一個線程訪問的時候,需要加上鎖以防止另外的線程對它進行訪問,以實現(xiàn)資源的獨占。在一個時刻只能有一個線程掌握某個互斥鎖,擁有上鎖狀態(tài)的線程才能夠對共享資源進行操作。若其他線程希望上鎖一個已經(jīng)上鎖了的互斥鎖,則該線程就會掛起,直到上鎖的線程釋放掉互斥鎖為止。
1. 互斥鎖介紹
在編程中,引入了對象互斥鎖的概念,來保證共享數(shù)據(jù)操作的完整性。每個對象都對應于一個可稱為" 互斥鎖" 的標記,這個標記用來保證在任一時刻,只能有一個線程訪問該對象。
linux系統(tǒng)下定義了一套專門用于線程互斥的mutex函數(shù)。
mutex 是一種簡單的加鎖的方法來控制對共享資源的存取,這個互斥鎖只有兩種狀態(tài)(上鎖和解鎖),可以把互斥鎖看作某種意義上的全局變量。
總結: 互斥鎖可以保護某個資源同時只能被一個線程所使用。
2. 互斥鎖相關的函數(shù)
#include //銷毀互斥鎖 int pthread_mutex_destroy(pthread_mutex_t *mutex); //初始化互斥鎖 int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); //上鎖: 阻塞方式 int pthread_mutex_lock(pthread_mutex_t *mutex); //上鎖: 非阻塞方式 int pthread_mutex_trylock(pthread_mutex_t *mutex); //解鎖 int pthread_mutex_unlock(pthread_mutex_t *mutex); 說明: 對于Linux下的信號量/讀寫鎖文件進行編譯,需要在編譯選項中指明-D_GNU_SOURCE 否則用gcc編譯就會出現(xiàn) PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP未聲明(在此函數(shù)內第一次使用) 這樣的提示。 例如: $ gcc app.c -lpthread -D_GNU_SOURCE
2.1 初始化互斥鎖
頭文件 #include 定義函數(shù) int pthread_mutex_init( pthread_mutex_t *mutex, const pthread_mutex_attr_t* attr ); 函數(shù)說明 該函數(shù)初始化一個互斥體變量,如果參數(shù)attr 為NULL,則互斥體變量mutex 使用默認的屬性。
2.2 銷毀互斥鎖
頭文件 #include 定義函數(shù) int pthread_mutex_destroy ( pthread_mutex_t *mutex ); 函數(shù)說明 該函數(shù)用來釋放分配給參數(shù)mutex 的資源。 返回值 調用成功時返回值為 0, 否則返回一個非0 的錯誤代碼。
2.3 上鎖
頭文件 #include 定義函數(shù) int pthread_mutex_lock( pthread_mutex_t *mutex ); 函數(shù)說明 該函數(shù)用來鎖住互斥體變量。如果參數(shù)mutex 所指的互斥體已經(jīng)被鎖住了,那么發(fā)出調用的線程將被阻塞直到其他線程對mutex 解鎖。 如果上鎖成功,將返回0。
2.4 嘗試上鎖-非阻塞
表頭文件 #include 定義函數(shù) int pthread_mutex_trylock( pthread_t *mutex ); 函數(shù)說明 該函數(shù)用來鎖住mutex 所指定的互斥體,但不阻塞。 返回值 如果該互斥體已經(jīng)被上鎖,該調用不會阻塞等待,而會返回一個錯誤代碼。 如果上鎖成功,將返回0.
2.5 解鎖
頭文件 #include 定義函數(shù) int pthread_mutex_unlock( pthread_mutex_t *mutex ); 函數(shù)說明 該函數(shù)用來對一個互斥體解鎖。如果當前線程擁有參數(shù)mutex 所指定的互斥體,該調用將該互斥體解鎖。 如果解鎖成功,將返回0. 說明: 對同一個鎖多次解鎖沒有疊加效果,如果鎖是上鎖狀態(tài),那么多次解鎖也只有一次有效。
3. 互斥鎖框架運用模型
pthread_mutex_t mutex; void 線程1(void) { while(1) { //上鎖 pthread_mutex_lock(&mutex); .....主體代碼...... //解鎖 pthread_mutex_unlock(&mutex); } } void 線程2(void) { while(1) { //上鎖 pthread_mutex_lock(&mutex); .....主體代碼...... //解鎖 pthread_mutex_unlock(&mutex); } } int main(void) { //初始化互斥鎖 pthread_mutex_init(&mutex,NULL); .....主體代碼...... //銷毀互斥鎖 pthread_mutex_destroy(&mutex); }
4. 案例代碼: 對公共函數(shù)上鎖保護
下面代碼是兩個線程同時調用了一個打印函數(shù),分別打印: “123” “456”。
void print(char *p) { while(*p!='
void print(char *p) { while(*p!='\0') { printf("%c",*p++); sleep(1); } } void *thread1_func(void *arg) { print("123\n"); } void *thread2_func(void *arg) { print("456\n"); }
') { printf("%c",*p++); sleep(1); } } void *thread1_func(void *arg) { print("123\n"); } void *thread2_func(void *arg) { print("456\n"); }
如果不保護,默認的打印結果:
[wbyq@wbyq linux-share-dir]$ ./a.out 412536
預期的結果應該是打印1236連續(xù)在一起的,對于這種情況,就可以加鎖進行保護。
上鎖的示例代碼:
#include #include #include #include #include #include #include #include #include pthread_mutex_t mutex; void print(char *p) { while(*p!='#include #include #include #include #include #include #include #include #include pthread_mutex_t mutex; void print(char *p) { while(*p!='\0') { printf("%c",*p++); sleep(1); } } /* 線程工作函數(shù) */ void *thread_work_func(void *dev) { while(1) { //上鎖 pthread_mutex_lock(&mutex); print("123\n"); //解鎖 pthread_mutex_unlock(&mutex); usleep(1000*10); } } /* 線程工作函數(shù) */ void *thread_work_func2(void *dev) { //上鎖 pthread_mutex_lock(&mutex); print("456\n"); //解鎖 pthread_mutex_unlock(&mutex); usleep(1000*10); } int main(int argc,char **argv) { //初始化互斥鎖 pthread_mutex_init(&mutex,NULL); /*1. 創(chuàng)建子線程1*/ pthread_t thread_id; if(pthread_create(&thread_id,NULL,thread_work_func,NULL)!=0) { printf("子線程1創(chuàng)建失敗.\n"); return -1; } /*2. 創(chuàng)建子線程2*/ pthread_t thread_id2; if(pthread_create(&thread_id2,NULL,thread_work_func2,NULL)!=0) { printf("子線程2創(chuàng)建失敗.\n"); return -1; } /*3. 等待線程的介紹*/ pthread_join(thread_id,NULL); pthread_join(thread_id2,NULL); //銷毀互斥鎖 pthread_mutex_destroy(&mutex); return 0; }
') { printf("%c",*p++); sleep(1); } } /* 線程工作函數(shù) */ void *thread_work_func(void *dev) { while(1) { //上鎖 pthread_mutex_lock(&mutex); print("123\n"); //解鎖 pthread_mutex_unlock(&mutex); usleep(1000*10); } } /* 線程工作函數(shù) */ void *thread_work_func2(void *dev) { //上鎖 pthread_mutex_lock(&mutex); print("456\n"); //解鎖 pthread_mutex_unlock(&mutex); usleep(1000*10); } int main(int argc,char **argv) { //初始化互斥鎖 pthread_mutex_init(&mutex,NULL); /*1. 創(chuàng)建子線程1*/ pthread_t thread_id; if(pthread_create(&thread_id,NULL,thread_work_func,NULL)!=0) { printf("子線程1創(chuàng)建失敗.\n"); return -1; } /*2. 創(chuàng)建子線程2*/ pthread_t thread_id2; if(pthread_create(&thread_id2,NULL,thread_work_func2,NULL)!=0) { printf("子線程2創(chuàng)建失敗.\n"); return -1; } /*3. 等待線程的介紹*/ pthread_join(thread_id,NULL); pthread_join(thread_id2,NULL); //銷毀互斥鎖 pthread_mutex_destroy(&mutex); return 0; }

5. 案例代碼: 模擬火車票售賣系統(tǒng)(保護同一個全局變量)
下面代碼模擬一個火車票售賣系統(tǒng),此處不加鎖,可能會出現(xiàn)賣出負數(shù)票的情況。
#include #include int cnt = 121; //火車票,公共資源(全局) void* pthread1(void* args) { while(ticketcount > 0) { printf("窗口A開始售票,門票是:%d\n",cnt); sleep(2); cnt--; printf("窗口A售票結束,最后一張車票是:%d\n",cnt); } } void* pthread2(void* args) { while(cnt > 0) { printf("窗口B開始售票,車票是:%d\n",cnt); sleep(2); cnt--; printf("窗口B售票結束,最后一張車票是:%d\n",cnt); } } int main() { pthread_t pthid1 = 0; pthread_t pthid2 = 0; pthread_create(&pthid1,NULL,pthread1,NULL); pthread_create(&pthid2,NULL,pthread2,NULL); pthread_join(pthid1,NULL); pthread_join(pthid2,NULL); return 0; }
加鎖之后的火車售票系統(tǒng)
#include #include int cnt = 121; pthread_mutex_t lock; void* pthread1(void* args) { while(1) { pthread_mutex_lock(&lock); //因為要訪問全局的共享變量,所以就要加鎖 if(cnt > 0) //如果有票 { printf("窗口A開始售票,車票是:%d\n",cnt); sleep(2); cnt--; printf("窗口A售票結束,最后一張車票是:%d\n",cnt); } else { pthread_mutex_unlock(&lock); pthread_exit(NULL); } pthread_mutex_unlock(&lock); sleep(1); //要放到鎖的外面,讓另一個有時間鎖 } } void* pthread2(void* args) { while(1) { pthread_mutex_lock(&lock); if(cnt>0) { printf("窗口B開始售票--車票是:%d\n",cnt); sleep(2); cnt--; printf("窗口B售票結束,最后一張車票是:%d\n",cnt); } else { pthread_mutex_unlock(&lock); pthread_exit(NULL); } pthread_mutex_unlock(&lock); sleep(1); } } int main() { pthread_t pthid1 = 0; pthread_t pthid2 = 0; //初始化鎖 pthread_mutex_init(&lock,NULL); //創(chuàng)建線程 pthread_create(&pthid1,NULL,pthread1,NULL); pthread_create(&pthid2,NULL,pthread2,NULL); //等待線程結束 pthread_join(pthid1,NULL); pthread_join(pthid2,NULL); //銷毀鎖 pthread_mutex_destroy(&lock); return 0; }
Linux 任務調度
版權聲明:本文內容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內刪除侵權內容。
版權聲明:本文內容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內刪除侵權內容。