国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

多線程同步相關

2019-11-09 15:45:11
字體:
來源:轉載
供稿:網友

多線程的最大優點之一是數據的共享性,各個進程共享父進程處沿襲的數據段,可以方便的獲得、修改數據。但這也給多線程編程帶來了許多問題。 我們必須當心有多個不同的進程訪問相同的變量。許多函數是不可重入的,即同時不能運行一個函數的多個拷貝(除非使用不同的數據段)。 在函數中聲明的靜態變量常常帶來問題,函數的返回值也會有問題。因為如果返回的是函數內部靜態聲明的空間的地址, 則在一個線程調用該函數得到地址后使用該地址指向的數據時,別的線程可能調用此函數并修改了這一段數據。 為了保護變量,我們必須使用信號量、互斥等方法來保證我們對變量的正確使用。在Poxi標準中提供了互斥量,讀寫鎖,條件變量實現互斥訪問,下面意義探討。

互斥量

假設多個線程向同一個文件寫入數據,若對寫入的順序不加以管理和控制,那么最終生成的文件肯定是無法解析的。所以必須用互斥鎖來保證一段時間內只有一個線程在寫入文件,當一個線程寫入完成后再給下一線程寫入。

鎖的創建

鎖可以被動態或靜態創建,可以用宏PTHREAD_MUTEX_INITIALIZER來靜態的初始化鎖,采用這種方式比較容易理解,互斥鎖是pthread_mutex_t的結構體,而這個宏是一個結構常量,如下可以完成靜態的初始化:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

另外鎖可以用pthread_mutex_init函數動態的創建,函數原型如下:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t * attr)

NULL參數表明使用默認屬性,通常使用默認屬性。

鎖的屬性

互斥鎖的范圍:可以指定是該進程與其他進程的同步還是同一進程內不同的線程之間的同步。可以設置為PTHREAD_PROCESS_SHARE和PTHREAD_PROCESS_PRIVATE。默認是后者,表示進程內使用鎖。#include <pthread.h>int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared);int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,int pshared); 互斥鎖的類型:PTHREAD_MUTEX_TIMED_NP,這是缺省值,也就是普通鎖。當一個線程加鎖以后,其余請求鎖的線程將形成一個等待隊列,并在解鎖后按優先級獲得鎖。這種鎖策略保證了資源分配的公平性。 PTHREAD_MUTEX_RECURSIVE_NP,嵌套鎖,允許同一個線程對同一個鎖成功獲得多次,并通過多次unlock解鎖。如果是不同線程請求,則在加鎖線程解鎖時重新競爭。 PTHREAD_MUTEX_ERRORCHECK_NP,檢錯鎖,如果同一個線程請求同一個鎖,則返回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP類型動作相同。這樣就保證當不允許多次加鎖時不會出現最簡單情況下的死鎖。 PTHREAD_MUTEX_ADAPTIVE_NP,適應鎖,動作最簡單的鎖類型,僅等待解鎖后重新競爭。int pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type)int pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type)

鎖操作

//上鎖int pthread_mutex_lock(pthread_mutex_t *mutex)int pthread_mutex_trylock(pthread_mutex_t *mutex)//解鎖int pthread_mutex_unlock(pthread_mutex_t *mutex)

pthread_mutex_trylock()語義與pthread_mutex_lock()類似,不同的是在鎖已經被占據時返回EBUSY而不是掛起等待。

鎖銷毀

int pthread_mutex_destory(pthread_mutex_t *mutex)

當鎖沒有被鎖定時。可以通過調用pthread_mutex_destory釋放鎖占用的資源。

使用示列

#include <pthread.h> #include <stdio.h> pthread_mutex_t mutex ; void *print_msg(void *arg){ int i=0; pthread_mutex_lock(&mutex); for(i=0;i<15;i++){ printf("output : %d/n",i); usleep(100); } pthread_mutex_unlock(&mutex); } int main(int argc,char** argv){ pthread_t id1; pthread_t id2; pthread_mutex_init(&mutex,NULL); pthread_create(&id1,NULL,print_msg,NULL); pthread_create(&id2,NULL,print_msg,NULL); pthread_join(id1,NULL); pthread_join(id2,NULL); pthread_mutex_destroy(&mutex); return 1; }

讀寫鎖

多個線程可以同時獲得讀鎖(Reader-Writer lock in read mode),但是只有一個線程能夠獲得寫鎖(Reader-writer lock in write mode)。

讀寫鎖總共有三種狀態: 1. 一個或者多個線程獲得讀鎖,其他線程無法獲得寫鎖 2. 一個線程獲得寫鎖,其他線程無法獲得讀鎖 3. 沒有線程獲得此讀寫鎖

#include <pthread.h>//初始化一個讀寫鎖,pthread_rwlockattr_t通常設為NULLint pthread_rwlock_init( pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr)//銷毀一個讀寫鎖int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);//獲取讀鎖int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);//獲取寫鎖int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);//讀寫解鎖int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

條件變量

適用的場景為條件滿足立刻被喚醒,否則掛起等待。條件需要被mutex保護。 條件變量類型為pthread_cond_t,必須被初始化為PTHREAD_COND_INITIALIZER,等價于調用pthread_cond_init(…, NULL)

#include <pthread.h>//初始化一個條件變量,和互斥鎖一樣我們可以用它來設置條件變量是進程內可用還是進程間可用,默認值是 PTHREAD_ PROCESS_PRIVATE,即此條件變量被同一進程內的各個線程使用。int pthread_cond_init( pthread_cond_t *restrict cond, const pthread_condxattr_t *restrict attr)//銷毀一個條件變量int pthread_cond_destroy(pthread_cond_t *cond);//等待條件發生int pthread_cond_wait( pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);int pthread_cond_timedwait( pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);/*pthread_cond_timedwait類似,只是當等待超時的時候返回一個錯誤值ETIMEDOUT。超時的時間用timespec結構指定。struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */};注意timespec的時間是絕對時間而非相對時間,因此需要先調用gettimeofday函數獲得當前時間,再轉換成timespec結構,加上偏移量。*///條件滿足后喚醒等待的線程int pthread_cond_signal(pthread_cond_t *cond);int pthread_cond_broadcast(pthread_cond_t *cond);/*兩者的區別是前者會喚醒單個線程,而后者會喚醒多個線程。*/

示列

#include <stdio.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t counter_lock; pthread_cond_t counter_nonzero; int counter = 0; int estatus = -1; void *decrement_counter(void *argv); void *increment_counter(void *argv); /* thd1啟動等待某個條件滿足后,才開始運行。 thd2當thd1運行的條件滿足后,開始喚醒thd1運行。*/ int main(int argc, char **argv) { printf("counter: %d/n", counter); pthread_t thd1, thd2; int ret; ret = pthread_create(&thd1, NULL, decrement_counter, NULL); if(ret){ perror("del:/n"); return 1; } ret = pthread_create(&thd2, NULL, increment_counter, NULL); if(ret){ perror("inc: /n"); return 1; } int counter = 0; while(counter != 2){ printf("counter(main): %d/n", counter); sleep(4); counter++; } return 0; } void *decrement_counter(void *argv) { pthread_mutex_lock(&counter_lock); printf("counter(decrement): %d decrement_counter lock! /n", counter); while(counter == 0) pthread_cond_wait(&counter_nonzero, &counter_lock); //進入阻塞(wait),等待喚醒信號 printf("counter--(before): %d/n", counter); counter--; //等待signal激活后再執行 printf("counter--(after): %d/n", counter); printf("decrement_counter will unlock! /n"); pthread_mutex_unlock(&counter_lock); return &estatus; } void *increment_counter(void *argv){ //模擬thd1比等待線程后啟動 sleep(1); pthread_mutex_lock(&counter_lock); printf("counter(increment): %d increment_counter lock!/n", counter); printf("counter++(before): %d/n", counter); counter++; printf("counter++(after): %d/n", counter); //thd1運行的條件滿足后,開始喚醒掛起的thd1 if(counter != 0) pthread_cond_signal(&counter_nonzero); printf("increment_counter will unlock!/n"); pthread_mutex_unlock(&counter_lock); return &estatus; }

gcc -g -pthread cond.c -lpthread -o test 運行test后,輸出如下:

counter: 0counter(main): 0counter(decrement): 0 decrement_counter lock! counter(increment): 0 increment_counter lock!counter++(before): 0counter++(after): 1increment_counter will unlock!counter--(before): 1counter--(after): 0decrement_counter will unlock! counter(main): 1

是不是覺得上面的輸出有點不可思議?明明decrement_counter lock為什么increment_counter lock還能成功。關鍵在于 pthread_cond_wait(&counter_nonzero, &counter_lock)在等待是,實際上已經將鎖unlock,并將線程掛起在等待隊列,等待喚醒。pthread_cond_signal(&counter_nonzero); 實際上會對對應mutex一個lock操作。

線程數據

在單線程的程序里,有兩種基本的數據:全局變量局部變量。但在多線程程序里,還有第三種數據類型:線程數據(TSD: Thread-Specific Data)。

它和全局變量很象,在線程內部,各個函數可以象使用全局變量一樣調用它,但它對線程外部的其它線程是不可見的。例如我們常見的變量errno,它返回標準的出錯信息。它顯然不能是一個局部變量,幾乎每個函數都應該可以調用它;但它又不能是一個全局變量,否則在 A線程里輸出的很可能是B線程的出錯信息。

要實現諸如此類的變量,我們就必須使用線程數據。我們為每個線程數據創建一個鍵,它和這個鍵相關聯,在各個線程里,都使用這個鍵來指代線程數據,但在不同的線程里,這個鍵代表的數據是不同的,在同一個線程里,它代表同樣的數據內容。

/*實現功能,創建5個線程,每個線程將日志記錄到thread%d.log日志文件中*/#include <malloc.h>#include <pthread.h>#include <stdio.h>static pthread_key_t thread_log_key;void write_to_thread_log (const char* message){ //從鍵讀取線程數據 FILE* thread_log = (FILE*) pthread_getspecific (thread_log_key); fprintf (thread_log, "%s/n", message); }void close_thread_log (void* thread_log){ fclose ((FILE*) thread_log);}void* thread_function (void* args){ char thread_log_filename[20]; FILE* thread_log; sprintf (thread_log_filename, "thread%d.log", (int) pthread_self ()); thread_log = fopen (thread_log_filename, "w+"); //為當前線程的鍵指定線程數據 pthread_setspecific (thread_log_key, thread_log); //記錄日志 write_to_thread_log ("Thread starting."); return NULL; }int main (){ int i; pthread_t threads[5]; //創建一個鍵,close_thread_log為線程退出時調用的析構函數 pthread_key_create (&thread_log_key, close_thread_log); for (i = 0; i < 5; ++i) pthread_create (&(threads[i]), NULL, thread_function, NULL); for (i = 0; i < 5; ++i) pthread_join (threads[i], NULL); //銷毀鍵 pthread_key_delete(thread_log_key); return 0;}
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 鄂伦春自治旗| 清流县| 息烽县| 牡丹江市| 玛纳斯县| 高清| 拉孜县| 泗阳县| 青冈县| 金川县| 利津县| 轮台县| 巨野县| 富平县| 台中市| 阳高县| 南通市| 烟台市| 施甸县| 手游| 宾阳县| 金门县| 铁岭市| 阜城县| 镇原县| 颍上县| 嘉义县| 闸北区| 永丰县| 北辰区| 嘉鱼县| 江山市| 芦山县| 辽阳市| 东兰县| 大新县| 平阴县| 辉南县| 五常市| 娄底市| 芜湖县|