Linux多線程開發(十八)

Linux多線程開發(十八)

線程概述

線程概述

 

 線程和進程區別

 

 線程之間共享和非共享資源

 

 NPTL

 

線程創建

 

 

代碼

 1 /*
 2     一般情況下,main函數所在的線程我們稱之爲主線程(main線程),其餘創建的線程
 3     稱之爲子線程。
 4     程序中默認只有一個進程,fork()函數調用,2進行
 5     程序中默認只有一個線程,pthread_create()函數調用,2個線程。
 6 
 7     #include <pthread.h>
 8     int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
 9     void *(*start_routine) (void *), void *arg);
10 
11         - 功能:創建一個子線程
12         - 參數:
13             - thread:傳出參數,線程創建成功後,子線程的線程ID被寫到該變量中。
14             - attr : 設置線程的屬性,一般使用默認值,NULL
15             - start_routine : 函數指針,這個函數是子線程需要處理的邏輯代碼
16             - arg : 給第三個參數使用,傳參
17         - 返回值:
18             成功:0
19             失敗:返回錯誤號。這個錯誤號和之前errno不太一樣。
20             獲取錯誤號的信息:  char * strerror(int errnum);
21 
22 */
23 #include <stdio.h>
24 #include <pthread.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 void * callback(void * arg) {
29     printf("child thread...\n");
30     printf("arg value: %d\n", *(int *)arg);
31     return NULL;
32 }
33 
34 int main() {
35 
36     pthread_t tid;
37 
38     int num = 10;
39 
40     // 創建一個子線程
41     int ret = pthread_create(&tid, NULL, callback, (void *)&num);
42 
43     if(ret != 0) {
44         char * errstr = strerror(ret);
45         printf("error : %s\n", errstr);
46     } 
47 
48     for(int i = 0; i < 5; i++) {
49         printf("%d\n", i);
50     }
51 
52     sleep(1);
53 
54     return 0;   // exit(0);
55 }

 

終止線程

 

 

代碼

 1 /*
 2 
 3     #include <pthread.h>
 4     void pthread_exit(void *retval);
 5         功能:終止一個線程,在哪個線程中調用,就表示終止哪個線程
 6         參數:
 7             retval:需要傳遞一個指針,作爲一個返回值,可以在pthread_join()中獲取到。
 8 
 9     pthread_t pthread_self(void);
10         功能:獲取當前的線程的線程ID
11 
12     int pthread_equal(pthread_t t1, pthread_t t2);
13         功能:比較兩個線程ID是否相等
14         不同的操作系統,pthread_t類型的實現不一樣,有的是無符號的長整型,有的
15         是使用結構體去實現的。
16 */
17 #include <stdio.h>
18 #include <pthread.h>
19 #include <string.h>
20 
21 void * callback(void * arg) {
22     printf("child thread id : %ld\n", pthread_self());
23     return NULL;    // pthread_exit(NULL);
24 } 
25 
26 int main() {
27 
28     // 創建一個子線程
29     pthread_t tid;
30     int ret = pthread_create(&tid, NULL, callback, NULL);
31 
32     if(ret != 0) {
33         char * errstr = strerror(ret);
34         printf("error : %s\n", errstr);
35     }
36 
37     // 主線程
38     for(int i = 0; i < 5; i++) {
39         printf("%d\n", i);
40     }
41 
42     printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self());
43 
44     // 讓主線程退出,當主線程退出時,不會影響其他正常運行的線程。
45     pthread_exit(NULL);
46 
47     printf("main thread exit\n");
48 
49     return 0;   // exit(0);
50 }

 

pthread_self當前進程的id

 

連接已終止的線程

 

 

代碼

 1 /*
 2     #include <pthread.h>
 3     int pthread_join(pthread_t thread, void **retval);
 4         - 功能:和一個已經終止的線程進行連接
 5                 回收子線程的資源
 6                 這個函數是阻塞函數,調用一次只能回收一個子線程
 7                 一般在主線程中使用
 8         - 參數:
 9             - thread:需要回收的子線程的ID
10             - retval: 接收子線程退出時的返回值
11         - 返回值:
12             0 : 成功
13             非0 : 失敗,返回的錯誤號
14 */
15 
16 #include <stdio.h>
17 #include <pthread.h>
18 #include <string.h>
19 #include <unistd.h>
20 
21 int value = 10;
22 
23 void * callback(void * arg) {
24     printf("child thread id : %ld\n", pthread_self());
25     // sleep(3);
26     // return NULL; 
27     // int value = 10; // 局部變量
28     pthread_exit((void *)&value);   // return (void *)&value;
29 } 
30 
31 int main() {
32 
33     // 創建一個子線程
34     pthread_t tid;
35     int ret = pthread_create(&tid, NULL, callback, NULL);
36 
37     if(ret != 0) {
38         char * errstr = strerror(ret);
39         printf("error : %s\n", errstr);
40     }
41 
42     // 主線程
43     for(int i = 0; i < 5; i++) {
44         printf("%d\n", i);
45     }
46 
47     printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self());
48 
49     // 主線程調用pthread_join()回收子線程的資源
50     int * thread_retval;
51     ret = pthread_join(tid, (void **)&thread_retval);
52 
53     if(ret != 0) {
54         char * errstr = strerror(ret);
55         printf("error : %s\n", errstr);
56     }
57 
58     printf("exit data : %d\n", *thread_retval);
59 
60     printf("回收子線程資源成功!\n");
61 
62     // 讓主線程退出,當主線程退出時,不會影響其他正常運行的線程。
63     pthread_exit(NULL);
64 
65     return 0; 
66 }

 

線程的分離

代碼

 1 /*
 2     #include <pthread.h>
 3     int pthread_detach(pthread_t thread);
 4         - 功能:分離一個線程。被分離的線程在終止的時候,會自動釋放資源返回給系統。
 5           1.不能多次分離,會產生不可預料的行爲。
 6           2.不能去連接一個已經分離的線程,會報錯。
 7         - 參數:需要分離的線程的ID
 8         - 返回值:
 9             成功:0
10             失敗:返回錯誤號
11 */
12 #include <stdio.h>
13 #include <pthread.h>
14 #include <string.h>
15 #include <unistd.h>
16 
17 void * callback(void * arg) {
18     printf("chid thread id : %ld\n", pthread_self());
19     return NULL;
20 }
21 
22 int main() {
23 
24     // 創建一個子線程
25     pthread_t tid;
26 
27     int ret = pthread_create(&tid, NULL, callback, NULL);
28     if(ret != 0) {
29         char * errstr = strerror(ret);
30         printf("error1 : %s\n", errstr);
31     }
32 
33     // 輸出主線程和子線程的id
34     printf("tid : %ld, main thread id : %ld\n", tid, pthread_self());
35 
36     // 設置子線程分離,子線程分離後,子線程結束時對應的資源就不需要主線程釋放
37     ret = pthread_detach(tid);
38     if(ret != 0) {
39         char * errstr = strerror(ret);
40         printf("error2 : %s\n", errstr);
41     }
42 
43     // 設置分離後,對分離的子線程進行連接 pthread_join()
44     // ret = pthread_join(tid, NULL);
45     // if(ret != 0) {
46     //     char * errstr = strerror(ret);
47     //     printf("error3 : %s\n", errstr);
48     // }
49 
50     pthread_exit(NULL);
51 
52     return 0;
53 }

 

線程操作

 

 

線程取消

pthread_cancel:取消線程或者是讓線程終止

是遇到終止點才取消,沒遇到終止點不取消。

 

代碼

 1 /*
 2     #include <pthread.h>
 3     int pthread_cancel(pthread_t thread);
 4         - 功能:取消線程(讓線程終止)
 5             取消某個線程,可以終止某個線程的運行,
 6             但是並不是立馬終止,而是當子線程執行到一個取消點,線程纔會終止。
 7             取消點:系統規定好的一些系統調用,我們可以粗略的理解爲從用戶區到內核區的切換,這個位置稱之爲取消點。
 8 */
 9 
10 #include <stdio.h>
11 #include <pthread.h>
12 #include <string.h>
13 #include <unistd.h>
14 
15 void * callback(void * arg) {
16     printf("chid thread id : %ld\n", pthread_self());
17     for(int i = 0; i < 5; i++) {
18         printf("child : %d\n", i);
19     }
20     return NULL;
21 }
22 
23 int main() {
24     
25     // 創建一個子線程
26     pthread_t tid;
27 
28     int ret = pthread_create(&tid, NULL, callback, NULL);
29     if(ret != 0) {
30         char * errstr = strerror(ret);
31         printf("error1 : %s\n", errstr);
32     }
33 
34     // 取消線程
35     pthread_cancel(tid);
36 
37     for(int i = 0; i < 5; i++) {
38         printf("%d\n", i);
39     }
40 
41     // 輸出主線程和子線程的id
42     printf("tid : %ld, main thread id : %ld\n", tid, pthread_self());
43 
44     
45     pthread_exit(NULL);
46 
47     return 0;
48 }

 

線程屬性

 

 

 

 

代碼

 1 /*
 2     int pthread_attr_init(pthread_attr_t *attr);
 3         - 初始化線程屬性變量
 4 
 5     int pthread_attr_destroy(pthread_attr_t *attr);
 6         - 釋放線程屬性的資源
 7 
 8     int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
 9         - 獲取線程分離的狀態屬性
10 
11     int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
12         - 設置線程分離的狀態屬性
13 */     
14 
15 #include <stdio.h>
16 #include <pthread.h>
17 #include <string.h>
18 #include <unistd.h>
19 
20 void * callback(void * arg) {
21     printf("chid thread id : %ld\n", pthread_self());
22     return NULL;
23 }
24 
25 int main() {
26 
27     // 創建一個線程屬性變量
28     pthread_attr_t attr;
29     // 初始化屬性變量
30     pthread_attr_init(&attr);
31 
32     // 設置屬性
33     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
34 
35     // 創建一個子線程
36     pthread_t tid;
37 
38     int ret = pthread_create(&tid, &attr, callback, NULL);
39     if(ret != 0) {
40         char * errstr = strerror(ret);
41         printf("error1 : %s\n", errstr);
42     }
43 
44     // 獲取線程的棧的大小
45     size_t size;
46     pthread_attr_getstacksize(&attr, &size);
47     printf("thread stack size : %ld\n", size);
48 
49     // 輸出主線程和子線程的id
50     printf("tid : %ld, main thread id : %ld\n", tid, pthread_self());
51 
52     // 釋放線程屬性資源
53     pthread_attr_destroy(&attr);
54 
55     pthread_exit(NULL);
56 
57     return 0;
58 }

 

線程同步

 

 

 

 

 

 

代碼

 1 /*
 2     使用多線程實現買票的案例。
 3     有3個窗口,一共是100張票。
 4 */
 5 
 6 #include <stdio.h>
 7 #include <pthread.h>
 8 #include <unistd.h>
 9 
10 // 全局變量,所有的線程都共享這一份資源。
11 int tickets = 100;
12 
13 void * sellticket(void * arg) {
14     // 賣票
15     while(tickets > 0) {
16         usleep(6000);
17         printf("%ld 正在賣第 %d 張門票\n", pthread_self(), tickets);
18         tickets--;
19     }
20     return NULL;
21 }
22 
23 int main() {
24 
25     // 創建3個子線程
26     pthread_t tid1, tid2, tid3;
27     pthread_create(&tid1, NULL, sellticket, NULL);
28     pthread_create(&tid2, NULL, sellticket, NULL);
29     pthread_create(&tid3, NULL, sellticket, NULL);
30 
31     // 回收子線程的資源,阻塞
32     pthread_join(tid1, NULL);
33     pthread_join(tid2, NULL);
34     pthread_join(tid3, NULL);
35 
36     // 設置線程分離。
37     // pthread_detach(tid1);
38     // pthread_detach(tid2);
39     // pthread_detach(tid3);
40 
41     pthread_exit(NULL); // 退出主線程
42 
43     return 0;
44 }

 

注意:分離後join,會報錯。

usleep(3000):3000微秒。

 

互斥鎖

互斥量

 

 

 

 

 

 

 

代碼

 1 /*
 2     互斥量的類型 pthread_mutex_t
 3     int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
 4         - 初始化互斥量
 5         - 參數 :
 6             - mutex : 需要初始化的互斥量變量
 7             - attr : 互斥量相關的屬性,NULL
 8         - restrict : C語言的修飾符,被修飾的指針,不能由另外的一個指針進行操作。
 9             pthread_mutex_t *restrict mutex = xxx;
10             pthread_mutex_t * mutex1 = mutex;
11 
12     int pthread_mutex_destroy(pthread_mutex_t *mutex);
13         - 釋放互斥量的資源
14 
15     int pthread_mutex_lock(pthread_mutex_t *mutex);
16         - 加鎖,阻塞的,如果有一個線程加鎖了,那麼其他的線程只能阻塞等待
17 
18     int pthread_mutex_trylock(pthread_mutex_t *mutex);
19         - 嘗試加鎖,如果加鎖失敗,不會阻塞,會直接返回。
20 
21     int pthread_mutex_unlock(pthread_mutex_t *mutex);
22         - 解鎖
23 */
24 #include <stdio.h>
25 #include <pthread.h>
26 #include <unistd.h>
27 
28 // 全局變量,所有的線程都共享這一份資源。
29 int tickets = 1000;
30 
31 // 創建一個互斥量
32 pthread_mutex_t mutex;
33 
34 void * sellticket(void * arg) {
35 
36     // 賣票
37     while(1) {
38 
39         // 加鎖
40         pthread_mutex_lock(&mutex);
41 
42         if(tickets > 0) {
43             usleep(6000);
44             printf("%ld 正在賣第 %d 張門票\n", pthread_self(), tickets);
45             tickets--;
46         }else {
47             // 解鎖
48             pthread_mutex_unlock(&mutex);
49             break;
50         }
51 
52         // 解鎖
53         pthread_mutex_unlock(&mutex);
54     }
55 
56     
57 
58     return NULL;
59 }
60 
61 int main() {
62 
63     // 初始化互斥量
64     pthread_mutex_init(&mutex, NULL);
65 
66     // 創建3個子線程
67     pthread_t tid1, tid2, tid3;
68     pthread_create(&tid1, NULL, sellticket, NULL);
69     pthread_create(&tid2, NULL, sellticket, NULL);
70     pthread_create(&tid3, NULL, sellticket, NULL);
71 
72     // 回收子線程的資源,阻塞
73     pthread_join(tid1, NULL);
74     pthread_join(tid2, NULL);
75     pthread_join(tid3, NULL);
76 
77     pthread_exit(NULL); // 退出主線程
78 
79     // 釋放互斥量資源
80     pthread_mutex_destroy(&mutex);
81 
82     return 0;
83 }

 

死鎖

死鎖

 

 

代碼

deadlock.c

 1 #include <stdio.h>
 2 #include <pthread.h>
 3 #include <unistd.h>
 4 
 5 // 全局變量,所有的線程都共享這一份資源。
 6 int tickets = 1000;
 7 
 8 // 創建一個互斥量
 9 pthread_mutex_t mutex;
10 
11 void * sellticket(void * arg) {
12 
13     // 賣票
14     while(1) {
15 
16         // 加鎖
17         pthread_mutex_lock(&mutex);
18         pthread_mutex_lock(&mutex);
19 
20         if(tickets > 0) {
21             usleep(6000);
22             printf("%ld 正在賣第 %d 張門票\n", pthread_self(), tickets);
23             tickets--;
24         }else {
25             // 解鎖
26             pthread_mutex_unlock(&mutex);
27             break;
28         }
29 
30         // 解鎖
31         pthread_mutex_unlock(&mutex);
32         pthread_mutex_unlock(&mutex);
33     }
34 
35     return NULL;
36 }
37 
38 int main() {
39 
40     // 初始化互斥量
41     pthread_mutex_init(&mutex, NULL);
42 
43     // 創建3個子線程
44     pthread_t tid1, tid2, tid3;
45     pthread_create(&tid1, NULL, sellticket, NULL);
46     pthread_create(&tid2, NULL, sellticket, NULL);
47     pthread_create(&tid3, NULL, sellticket, NULL);
48 
49     // 回收子線程的資源,阻塞
50     pthread_join(tid1, NULL);
51     pthread_join(tid2, NULL);
52     pthread_join(tid3, NULL);
53 
54     pthread_exit(NULL); // 退出主線程
55 
56     // 釋放互斥量資源
57     pthread_mutex_destroy(&mutex);
58 
59     return 0;
60 }

 

deadlock1.c(多線程多鎖造成的死鎖場景)

 1 #include <stdio.h>
 2 #include <pthread.h>
 3 #include <unistd.h>
 4 
 5 // 創建2個互斥量
 6 pthread_mutex_t mutex1, mutex2;
 7 
 8 void * workA(void * arg) {
 9 
10     pthread_mutex_lock(&mutex1);
11     sleep(1);
12     pthread_mutex_lock(&mutex2);
13 
14     printf("workA....\n");
15 
16     pthread_mutex_unlock(&mutex2);
17     pthread_mutex_unlock(&mutex1);
18     return NULL;
19 }
20 
21 
22 void * workB(void * arg) {
23     pthread_mutex_lock(&mutex2);
24     sleep(1);
25     pthread_mutex_lock(&mutex1);
26 
27     printf("workB....\n");
28 
29     pthread_mutex_unlock(&mutex1);
30     pthread_mutex_unlock(&mutex2);
31 
32     return NULL;
33 }
34 
35 int main() {
36 
37     // 初始化互斥量
38     pthread_mutex_init(&mutex1, NULL);
39     pthread_mutex_init(&mutex2, NULL);
40 
41     // 創建2個子線程
42     pthread_t tid1, tid2;
43     pthread_create(&tid1, NULL, workA, NULL);
44     pthread_create(&tid2, NULL, workB, NULL);
45 
46     // 回收子線程資源
47     pthread_join(tid1, NULL);
48     pthread_join(tid2, NULL);
49 
50     // 釋放互斥量資源
51     pthread_mutex_destroy(&mutex1);
52     pthread_mutex_destroy(&mutex2);
53 
54     return 0;
55 }

 

讀寫鎖

 

 

 

 

代碼

 1 /*
 2     讀寫鎖的類型 pthread_rwlock_t
 3     int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
 4     int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
 5     int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
 6     int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
 7     int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
 8     int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
 9     int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
10 
11     案例:8個線程操作同一個全局變量。
12     3個線程不定時寫這個全局變量,5個線程不定時的讀這個全局變量
13 */
14 
15 #include <stdio.h>
16 #include <pthread.h>
17 #include <unistd.h>
18 
19 // 創建一個共享數據
20 int num = 1;
21 // pthread_mutex_t mutex;
22 pthread_rwlock_t rwlock;
23 
24 void * writeNum(void * arg) {
25 
26     while(1) {
27         pthread_rwlock_wrlock(&rwlock);
28         num++;
29         printf("++write, tid : %ld, num : %d\n", pthread_self(), num);
30         pthread_rwlock_unlock(&rwlock);
31         usleep(100);
32     }
33 
34     return NULL;
35 }
36 
37 void * readNum(void * arg) {
38 
39     while(1) {
40         pthread_rwlock_rdlock(&rwlock);
41         printf("===read, tid : %ld, num : %d\n", pthread_self(), num);
42         pthread_rwlock_unlock(&rwlock);
43         usleep(100);
44     }
45 
46     return NULL;
47 }
48 
49 int main() {
50 
51    pthread_rwlock_init(&rwlock, NULL);
52 
53     // 創建3個寫線程,5個讀線程
54     pthread_t wtids[3], rtids[5];
55     for(int i = 0; i < 3; i++) {
56         pthread_create(&wtids[i], NULL, writeNum, NULL);
57     }
58 
59     for(int i = 0; i < 5; i++) {
60         pthread_create(&rtids[i], NULL, readNum, NULL);
61     }
62 
63     // 設置線程分離
64     for(int i = 0; i < 3; i++) {
65        pthread_detach(wtids[i]);
66     }
67 
68     for(int i = 0; i < 5; i++) {
69          pthread_detach(rtids[i]);
70     }
71 
72     pthread_exit(NULL);
73 
74     pthread_rwlock_destroy(&rwlock);
75 
76     return 0;
77 }

 

生產者和消費者模型

該模型採用鏈表結構實現。

 

 

 代碼

 1 /*
 2     生產者消費者模型(粗略的版本)
 3 */
 4 #include <stdio.h>
 5 #include <pthread.h>
 6 #include <stdlib.h>
 7 #include <unistd.h>
 8 
 9 // 創建一個互斥量
10 pthread_mutex_t mutex;
11 
12 struct Node{
13     int num;
14     struct Node *next;
15 };
16 
17 // 頭結點
18 struct Node * head = NULL;
19 
20 void * producer(void * arg) {
21 
22     // 不斷的創建新的節點,添加到鏈表中
23     while(1) {
24         pthread_mutex_lock(&mutex);
25         struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
26         newNode->next = head;
27         head = newNode;
28         newNode->num = rand() % 1000;
29         printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
30         pthread_mutex_unlock(&mutex);
31         usleep(100);
32     }
33 
34     return NULL;
35 }
36 
37 void * customer(void * arg) {
38 
39     while(1) {
40         pthread_mutex_lock(&mutex);
41         // 保存頭結點的指針
42         struct Node * tmp = head;
43 
44         // 判斷是否有數據
45         if(head != NULL) {
46             // 有數據
47             head = head->next;
48             printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
49             free(tmp);
50             pthread_mutex_unlock(&mutex);
51             usleep(100);
52         } else {
53             // 沒有數據
54             pthread_mutex_unlock(&mutex);
55         }
56     }
57     return  NULL;
58 }
59 
60 int main() {
61 
62     pthread_mutex_init(&mutex, NULL);
63 
64     // 創建5個生產者線程,和5個消費者線程
65     pthread_t ptids[5], ctids[5];
66 
67     for(int i = 0; i < 5; i++) {
68         pthread_create(&ptids[i], NULL, producer, NULL);
69         pthread_create(&ctids[i], NULL, customer, NULL);
70     }
71 
72     for(int i = 0; i < 5; i++) {
73         pthread_detach(ptids[i]);
74         pthread_detach(ctids[i]);
75     }
76 
77     while(1) {
78         sleep(10);
79     }
80 
81     pthread_mutex_destroy(&mutex);
82 
83     pthread_exit(NULL);
84 
85     return 0;
86 }

 

條件變量

 

 注意:解決線程同步的方法有互斥鎖和讀寫鎖,條件變量不是鎖,條件變量不用來解決線程同步的問題。

 

 

代碼

  1 /*
  2     條件變量的類型 pthread_cond_t
  3     int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
  4     int pthread_cond_destroy(pthread_cond_t *cond);
  5     int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
  6         - 等待,調用了該函數,線程會阻塞。
  7     int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
  8         - 等待多長時間,調用了這個函數,線程會阻塞,直到指定的時間結束。
  9     int pthread_cond_signal(pthread_cond_t *cond);
 10         - 喚醒一個或者多個等待的線程
 11     int pthread_cond_broadcast(pthread_cond_t *cond);
 12         - 喚醒所有的等待的線程
 13 */
 14 #include <stdio.h>
 15 #include <pthread.h>
 16 #include <stdlib.h>
 17 #include <unistd.h>
 18 
 19 // 創建一個互斥量
 20 pthread_mutex_t mutex;
 21 // 創建條件變量
 22 pthread_cond_t cond;
 23 
 24 struct Node{
 25     int num;
 26     struct Node *next;
 27 };
 28 
 29 // 頭結點
 30 struct Node * head = NULL;
 31 
 32 void * producer(void * arg) {
 33 
 34     // 不斷的創建新的節點,添加到鏈表中
 35     while(1) {
 36         pthread_mutex_lock(&mutex);
 37         struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
 38         newNode->next = head;
 39         head = newNode;
 40         newNode->num = rand() % 1000;
 41         printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
 42         
 43         // 只要生產了一個,就通知消費者消費
 44         pthread_cond_signal(&cond);
 45 
 46         pthread_mutex_unlock(&mutex);
 47         usleep(100);
 48     }
 49 
 50     return NULL;
 51 }
 52 
 53 void * customer(void * arg) {
 54 
 55     while(1) {
 56         pthread_mutex_lock(&mutex);
 57         // 保存頭結點的指針
 58         struct Node * tmp = head;
 59         // 判斷是否有數據
 60         if(head != NULL) {
 61             // 有數據
 62             head = head->next;
 63             printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
 64             free(tmp);
 65             pthread_mutex_unlock(&mutex);
 66             usleep(100);
 67         } else {
 68             // 沒有數據,需要等待
 69             // 當這個函數調用阻塞的時候,會對互斥鎖進行解鎖,當不阻塞的,繼續向下執行,會重新加鎖。
 70             pthread_cond_wait(&cond, &mutex);
 71             pthread_mutex_unlock(&mutex);
 72         }
 73     }
 74     return  NULL;
 75 }
 76 
 77 int main() {
 78 
 79     pthread_mutex_init(&mutex, NULL);
 80     pthread_cond_init(&cond, NULL);
 81 
 82     // 創建5個生產者線程,和5個消費者線程
 83     pthread_t ptids[5], ctids[5];
 84 
 85     for(int i = 0; i < 5; i++) {
 86         pthread_create(&ptids[i], NULL, producer, NULL);
 87         pthread_create(&ctids[i], NULL, customer, NULL);
 88     }
 89 
 90     for(int i = 0; i < 5; i++) {
 91         pthread_detach(ptids[i]);
 92         pthread_detach(ctids[i]);
 93     }
 94 
 95     while(1) {
 96         sleep(10);
 97     }
 98 
 99     pthread_mutex_destroy(&mutex);
100     pthread_cond_destroy(&cond);
101 
102     pthread_exit(NULL);
103 
104     return 0;
105 }

 

信號量(信號燈)

注意:單獨使用信號量不能讓線程安全,需要配合使用互斥鎖才能保證線程的安全。

信號量的類型

 代碼

  1 /*
  2     信號量的類型 sem_t
  3     int sem_init(sem_t *sem, int pshared, unsigned int value);
  4         - 初始化信號量
  5         - 參數:
  6             - sem : 信號量變量的地址
  7             - pshared : 0 用在線程間 ,非0 用在進程間
  8             - value : 信號量中的值
  9 
 10     int sem_destroy(sem_t *sem);
 11         - 釋放資源
 12 
 13     int sem_wait(sem_t *sem);
 14         - 對信號量加鎖,調用一次對信號量的值-1,如果值爲0,就阻塞
 15 
 16     int sem_trywait(sem_t *sem);
 17 
 18     int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
 19     int sem_post(sem_t *sem);
 20         - 對信號量解鎖,調用一次對信號量的值+1
 21 
 22     int sem_getvalue(sem_t *sem, int *sval);
 23 
 24     sem_t psem;
 25     sem_t csem;
 26     init(psem, 0, 8);
 27     init(csem, 0, 0);
 28 
 29     producer() {
 30         sem_wait(&psem);
 31         sem_post(&csem)
 32     }
 33 
 34     customer() {
 35         sem_wait(&csem);
 36         sem_post(&psem)
 37     }
 38 
 39 */
 40 
 41 #include <stdio.h>
 42 #include <pthread.h>
 43 #include <stdlib.h>
 44 #include <unistd.h>
 45 #include <semaphore.h>
 46 
 47 // 創建一個互斥量
 48 pthread_mutex_t mutex;
 49 // 創建兩個信號量
 50 sem_t psem;
 51 sem_t csem;
 52 
 53 struct Node{
 54     int num;
 55     struct Node *next;
 56 };
 57 
 58 // 頭結點
 59 struct Node * head = NULL;
 60 
 61 void * producer(void * arg) {
 62 
 63     // 不斷的創建新的節點,添加到鏈表中
 64     while(1) {
 65         sem_wait(&psem);
 66         pthread_mutex_lock(&mutex);
 67         struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
 68         newNode->next = head;
 69         head = newNode;
 70         newNode->num = rand() % 1000;
 71         printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
 72         pthread_mutex_unlock(&mutex);
 73         sem_post(&csem);
 74     }
 75 
 76     return NULL;
 77 }
 78 
 79 void * customer(void * arg) {
 80 
 81     while(1) {
 82         sem_wait(&csem);
 83         pthread_mutex_lock(&mutex);
 84         // 保存頭結點的指針
 85         struct Node * tmp = head;
 86         head = head->next;
 87         printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
 88         free(tmp);
 89         pthread_mutex_unlock(&mutex);
 90         sem_post(&psem);
 91        
 92     }
 93     return  NULL;
 94 }
 95 
 96 int main() {
 97 
 98     pthread_mutex_init(&mutex, NULL);
 99     sem_init(&psem, 0, 8);
100     sem_init(&csem, 0, 0);
101 
102     // 創建5個生產者線程,和5個消費者線程
103     pthread_t ptids[5], ctids[5];
104 
105     for(int i = 0; i < 5; i++) {
106         pthread_create(&ptids[i], NULL, producer, NULL);
107         pthread_create(&ctids[i], NULL, customer, NULL);
108     }
109 
110     for(int i = 0; i < 5; i++) {
111         pthread_detach(ptids[i]);
112         pthread_detach(ctids[i]);
113     }
114 
115     while(1) {
116         sleep(10);
117     }
118 
119     pthread_mutex_destroy(&mutex);
120 
121     pthread_exit(NULL);
122 
123     return 0;
124 }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章