閱讀421 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Linux多線程編程小結

1.Linux進程與線程

Linux進程創建一個新線程時,線程將擁有自己的棧(因為線程有自己的局部變量),但與它的創建者共享全局變量、文件描述符、信號句柄和當前目錄狀態。

Linux通過fork創建子進程與創建線程之間是有區別的:fork創建出該進程的一份拷貝,這個新進程擁有自己的變量和自己的PID,它的時間調度是獨立的,它的執行幾乎完全獨立於父進程。

進程可以看成一個資源的基本單位,而線程是程序調度的基本單位,一個進程內部的線程之間共享進程獲得的時間片。

2._REENTRANT宏

在一個多線程程序裏,默認情況下,隻有一個errno變量供所有的線程共享。在一個線程準備獲取剛才的錯誤代碼時,該變量很容易被另一個線程中的函數調用所改變。類似的問題還存在於fputs之類的函數中,這些函數通常用一個單獨的全局性區域來緩存輸出數據。

為解決這個問題,需要使用可重入的例程。可重入代碼可以被多次調用而仍然工作正常。編寫的多線程程序,通過定義宏_REENTRANT來告訴編譯器我們需要可重入功能,這個宏的定義必須出現於程序中的任何#include語句之前。

_REENTRANT為我們做三件事情,並且做的非常優雅:

(1)它會對部分函數重新定義它們的可安全重入的版本,這些函數名字一般不會發生改變,隻是會在函數名後麵添加_r字符串,如函數名gethostbyname變成gethostbyname_r。

(2)stdio.h中原來以宏的形式實現的一些函數將變成可安全重入函數。

(3)在error.h中定義的變量error現在將成為一個函數調用,它能夠以一種安全的多線程方式來獲取真正的errno的值。

3.線程的基本函數

大多數pthread_XXX係列的函數在失敗時,並未遵循UNIX函數的慣例返回-1,這種情況在UNIX函數中屬於一少部分。如果調用成功,則返回值是0,如果失敗則返回錯誤代碼。

1.線程創建:

#include <pthread.h>

int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

參數說明:

thread:指向pthread_create類型的指針,用於引用新創建的線程。

attr:用於設置線程的屬性,一般不需要特殊的屬性,所以可以簡單地設置為NULL。

*(*start_routine)(void *):傳遞新線程所要執行的函數地址。

arg:新線程所要執行的函數的參數。

調用如果成功,則返回值是0,如果失敗則返回錯誤代碼。

2.線程終止

#include <pthread.h>

void pthread_exit(void *retval);

參數說明:

retval:返回指針,指向線程向要返回的某個對象。

線程通過調用pthread_exit函數終止執行,並返回一個指向某對象的指針。注意:絕不能用它返回一個指向局部變量的指針,因為線程調用該函數後,這個局部變量就不存在了,這將引起嚴重的程序漏洞。

3.線程同步

#include <pthread.h>

int pthread_join(pthread_t th, void **thread_return);

參數說明:

th:將要等待的張璐,線程通過pthread_create返回的標識符來指定。

thread_return:一個指針,指向另一個指針,而後者指向線程的返回值。

一個簡單的多線程Demo(thread1.c):

view plaincopy to clipboardprint?

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. void *thread_function(void *arg); 
  6. char message[] = "Hello World"; 
  7. int main() 
  8. int res; 
  9.     pthread_t a_thread; 
  10. void *thread_result; 
  11.     res = pthread_create(&a_thread, NULL, thread_function, (void *)message); 
  12. if (res != 0) 
  13.     { 
  14.         perror("Thread creation failed!"); 
  15.         exit(EXIT_FAILURE); 
  16.     } 
  17.     printf("Waiting for thread to finish...\n"); 
  18.     res = pthread_join(a_thread, &thread_result); 
  19. if (res != 0) 
  20.     { 
  21.         perror("Thread join failed!\n"); 
  22.         exit(EXIT_FAILURE); 
  23.     } 
  24.     printf("Thread joined, it returned %s\n", (char *)thread_result); 
  25.     printf("Message is now %s\n", message); 
  26.     exit(EXIT_FAILURE); 
  27. void *thread_function(void *arg) 
  28.     printf("thread_function is running. Argument was %s\n", (char *)arg); 
  29.     sleep(3); 
  30.     strcpy(message, "Bye!"); 
  31.     pthread_exit("Thank you for your CPU time!"); 

編譯這個程序時,需要定義宏_REENTRANT:

gcc -D_REENTRANT thread1.c -o thread1 –lpthread

運行這個程序:

$ ./thread1輸出:

thread_function is running. Argument was Hello World

Waiting for thread to finish...

Thread joined, it returned Thank you for your CPU time!

Message is now Bye!

這個例子值得我們去花時間理解,因為它將作為幾個例子的基礎。

pthread_exit(void *retval)本身返回的就是指向某個對象的指針,因此,pthread_join(pthread_t th, void **thread_return);中的thread_return是二級指針,指向線程返回值的指針。

可以看到,我們創建的新線程修改的數組message的值,而原先的線程也可以訪問該數組。如果我們調用的是fork而不是pthread_create,就不會有這樣的效果了。原因是fork創建子進程之後,子進程會拷貝父進程,兩者分離,相互不幹擾,而線程之間則是共享進程的相關資源。

4.線程的同時執行

接下來,我們來編寫一個程序,以驗證兩個線程的執行是同時進行的。當然,如果是在一個單處理器係統上,線程的同時執行就需要靠CPU在線程之間的快速切換來實現了。

我們的程序需要利用一個原理:即除了局部變量外,所有其他的變量在一個進程中的所有線程之間是共享的。

在這個程序中,我們是在兩個線程之間使用輪詢技術,這種方式稱為忙等待,所以它的效率會很低。在本文的後續部分,我們將介紹一種更好的解決辦法。

下麵的代碼中,兩個線程會不斷的輪詢判斷flag的值是否滿足各自的要求。

view plaincopy to clipboardprint?

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. int flag = 1; 
  5. void *thread_function(void *arg); 
  6. int main() 
  7. int res; 
  8.     pthread_t a_thread; 
  9. void *thread_result; 
  10. int count = 1; 
  11.     res = pthread_create(&a_thread, NULL, thread_function, NULL); 
  12. if (res != 0) 
  13.     { 
  14.         perror("Thread creation failed"); 
  15.         exit(EXIT_FAILURE); 
  16.     } 
  17. while (count++ <= 20) 
  18.     { 
  19. if (flag == 1) 
  20.         { 
  21.             printf ("1"); 
  22.             flag = 2; 
  23.         } 
  24. else
  25.         { 
  26.             sleep(1); 
  27.         } 
  28.     } 
  29.     printf("\nWaiting for thread to finish...\n"); 
  30.     res = pthread_join(a_thread, &thread_result); 
  31. if (res != 0) 
  32.     { 
  33.         perror("Thread join failed"); 
  34.         exit(EXIT_FAILURE); 
  35.     } 
  36.     exit(EXIT_SUCCESS); 
  37. void *thread_function(void *arg) 
  38. int count = 1; 
  39. while (count++ <= 20) 
  40.     { 
  41. if (flag == 2) 
  42.         { 
  43.             printf("2"); 
  44.             flag = 1; 
  45.         } 
  46. else
  47.         { 
  48.             sleep(1); 
  49.         } 
  50.     } 

編譯這個程序:

gcc -D_REENTRANT thread2.c -o thread2 –lpthread

運行這個程序:

$ ./thread2

121212121212121212

Waiting for thread to finish...

5.線程的同步

在上述示例中,我們采用輪詢的方式在兩個線程之間不停地切換是非常笨拙且沒有效率的實現方式,幸運的是,專門有一級設計好的函數為我們提供更好的控製線程執行和訪問代碼臨界區的方法。

本小節將介紹兩個線程同步的基本方法:信號量和互斥量。這兩種方法很相似,事實上,它們可以互相通過對方來實現。但在實際的應用中,對於一些情況,可能使用信號量或互斥量中的一個更符合問題的語義,並且效果更好。

5.1用信號量進行同步

1.信號量創建

#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);

參數說明:

sem:信號量對象。

pshared:控製信號量的類型,0表示這個信號量是當前進程的局部信號量,否則,這個信號量就可以在多個進程之間共享。

value:信號量的初始值。

2.信號量控製

#include <semaphore.h>

int sem_wait(sem_t *sem);

int sem_post(sem_t *sem);

sem_post的作用是以原子操作的方式給信號量的值加1。 
sem_wait的作用是以原子操作的方式給信號量的值減1,但它會等到信號量非0時才會開始減法操作。如果對值為0的信號量調用sem_wait,這個函數就會等待,直到有線程增加了該信號量的值使其不再為0。

3.信號量銷毀

#include <semaphore.h>

int sem_destory(sem_t *sem);

這個函數的作用是,用完信號量後對它進行清理,清理該信號量所擁有的資源。如果你試圖清理的信號量正被一些線程等待,就會收到一個錯誤。

與大多數Linux函數一樣,這些函數在成功時都返回0。

下麵編碼實現輸入字符串,統計每行的字符個數,以“end”結束輸入:

view plaincopy to clipboardprint?

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <semaphore.h>
  6. #define SIZE 1024
  7. void *thread_function(void *arg); 
  8. char buffer[SIZE]; 
  9. sem_t sem; 
  10. int main() 
  11. int res; 
  12.     pthread_t a_thread; 
  13. void *thread_result; 
  14.     res = sem_init(&sem, 0, 0); 
  15. if (res != 0) 
  16.     { 
  17.         perror("Sem init failed"); 
  18.         exit(EXIT_FAILURE); 
  19.     } 
  20.     res = pthread_create(&a_thread, NULL, thread_function, NULL); 
  21. if (res != 0) 
  22.     { 
  23.         perror("Thread create failed"); 
  24.         exit(EXIT_FAILURE); 
  25.     } 
  26.     printf("Input some text. Enter 'end' to finish\n"); 
  27. while (scanf("%s", buffer)) 
  28.     { 
  29.         sem_post(&sem); 
  30. if (strncmp("end", buffer, 3) == 0) 
  31. break; 
  32.     } 
  33.     printf ("\nWaiting for thread to finish...\n"); 
  34.     res = pthread_join(a_thread, &thread_result); 
  35. if (res != 0) 
  36.     { 
  37.         perror("Thread join failed"); 
  38.         exit(EXIT_FAILURE); 
  39.     } 
  40.     printf ("Thread join\n"); 
  41.     sem_destroy(&sem); 
  42.     exit(EXIT_SUCCESS); 
  43. void *thread_function(void *arg) 
  44.     sem_wait(&sem); 
  45. while (strncmp("end", buffer, 3) != 0) 
  46.     { 
  47.         printf("You input %d characters\n", strlen(buffer)); 
  48.         sem_wait(&sem); 
  49.     } 
  50.     pthread_exit(NULL); 

編譯這個程序:

gcc -D_REENTRANT thread2.c -o thread2 –lpthread

運行這個程序:

$ ./thread3

Input some text. Enter 'end' to finish

123

You input 3 characters

1234

You input 4 characters

12345

You input 5 characters

end

Waiting for thread to finish…

Thread join

通過使用信號量,我們阻塞了統計字符個數的線程,這個程序似乎對快速的文本輸入和悠閑的暫停都很適用,比之前的輪詢解決方案效率上有了本質的提高。

5.2用互斥量進行線程同步

另一種用在多線程程序中同步訪問的方法是使用互斥量。它允許程序員鎖住某個對象,使得每次隻能有一個線程訪問它。為了控製對關鍵代碼的訪問,必須在進入這段代碼之前鎖住一個互斥量,然後在完成操作之後解鎖它。

用於互斥量的基本函數和用於信號量的函數非常相似:

#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_unlock(pthread_mutex_t *mutex);

int pthread_mutex_destory(pthread_mutex_t *mutex);

與其他函數一樣,成功時返回0,失敗時將返回錯誤代碼,但這些函數並不設置errno,所以必須對函數的返回代碼進行檢查。互斥量的屬性設置這裏不討論,因此設置成NULL。

我們用互斥量來重寫剛才的代碼如下:

view plaincopy to clipboardprint?

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <semaphore.h>
  6. #define SIZE 1024
  7. char buffer[SIZE]; 
  8. void *thread_function(void *arg); 
  9. pthread_mutex_t mutex; 
  10. int main() 
  11. int res; 
  12.     pthread_t a_thread; 
  13. void *thread_result; 
  14.     res = pthread_mutex_init(&mutex, NULL); 
  15. if (res != 0) 
  16.     { 
  17.         perror("Mutex init failed!"); 
  18.         exit(EXIT_FAILURE); 
  19.     } 
  20.     res = pthread_create(&a_thread, NULL, thread_function, NULL); 
  21. if (res != 0) 
  22.     { 
  23.         perror("Thread create failed!"); 
  24.         exit(EXIT_FAILURE); 
  25.     } 
  26.     printf("Input some text. Enter 'end' to finish\n"); 
  27. while (1) 
  28.     { 
  29.         pthread_mutex_lock(&mutex); 
  30.         scanf("%s", buffer); 
  31.         pthread_mutex_unlock(&mutex); 
  32. if (strncmp("end", buffer, 3) == 0) 
  33. break; 
  34.         sleep(1); 
  35.     } 
  36.     res = pthread_join(a_thread, &thread_result); 
  37. if (res != 0) 
  38.     { 
  39.         perror("Thread join failed!"); 
  40.         exit(EXIT_FAILURE); 
  41.     } 
  42.     printf("Thread joined\n"); 
  43.     pthread_mutex_destroy(&mutex); 
  44.     exit(EXIT_SUCCESS); 
  45. void *thread_function(void *arg) 
  46.     sleep(1); 
  47. while (1) 
  48.     { 
  49.         pthread_mutex_lock(&mutex); 
  50.         printf("You input %d characters\n", strlen(buffer)); 
  51.         pthread_mutex_unlock(&mutex); 
  52. if (strncmp("end", buffer, 3) == 0) 
  53. break; 
  54.         sleep(1); 
  55.     } 

編譯這個程序:

gcc -D_REENTRANT thread4.c -o thread4 –lpthread

運行這個程序:

$ ./thread4

Input some text. Enter 'end' to finish

123

You input 3 characters

1234

You input 4 characters

12345

You input 5 characters

end

You input 3 characters

Thread joined

6.線程的屬性

之前我們並未談及到線程的屬性,可以控製的線程屬性是非常多的,這裏麵隻列舉一些常用的。

如在前麵的示例中,我們都使用的pthread_join同步線程,但其實有些情況下,我們並不需要。如:主線程為服務線程,而第二個線程為數據備份線程,備份工作完成之後,第二個線程可以直接終止了,它沒有必要再返回到主線程中。因此,我們可以創建一個“脫離線程”。

下麵介紹幾個常用的函數:

(1)int pthread_attr_init (pthread_attr_t* attr);

功能:對線程屬性變量的初始化。

attr:線程屬性。

函數返回值:成功:0,失敗:-1

(2) int pthread_attr_setscope (pthread_attr_t* attr, int scope);

功能:設置線程綁定屬性。

attr:線程屬性。

scope:PTHREAD_SCOPE_SYSTEM(綁定);PTHREAD_SCOPE_PROCESS(非綁定)

函數返回值:成功:0,失敗:-1

(3) int pthread_attr_setdetachstate (pthread_attr_t* attr, int detachstate);

功能:設置線程分離屬性。

attr:線程屬性。

detachstate:PTHREAD_CREATE_DETACHED(分離);PTHREAD_CREATE_JOINABLE(非分離)

函數返回值:成功:0,失敗:-1

(4) int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy);

功能:設置創建線程的調度策略。

attr:線程屬性;

policy:線程調度策略:SCHED_FIFO、SCHED_RR和SCHED_OTHER。

函數返回值:成功:0,失敗:-1

(5) int pthread_attr_setschedparam (pthread_attr_t* attr, struct sched_param* param);

功能:設置線程優先級。

attr:線程屬性。

param:線程優先級。

函數返回值:成功:0,失敗:-1

(6) int pthread_attr_destroy (pthread_attr_t* attr);

功能:對線程屬性變量的銷毀。

attr:線程屬性。

函數返回值:成功:0,失敗:-1

(7)其他

int pthread_attr_setguardsize(pthread_attr_t* attr,size_t guardsize);//設置新創建線程棧的保護區大小。

int pthread_attr_setinheritsched(pthread_attr_t* attr, int inheritsched);//決定怎樣設置新創建線程的調度屬性。

int pthread_attr_setstack(pthread_attr_t* attr, void* stackader,size_t stacksize);//兩者共同決定了線程棧的基地址以及堆棧的最小尺寸(以字節為單位)。

int pthread_attr_setstackaddr(pthread_attr_t* attr, void* stackader);//決定了新創建線程的棧的基地址。

int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize);//決定了新創建線程的棧的最小尺寸(以字節為單位)。

例:創建優先級為10的線程。

pthread_attr_t attr;

struct sched_param param;

pthread_attr_init(&attr);

pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); //綁定

pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); //分離

pthread_attr_setschedpolicy(&attr, SCHED_RR);

param.sched_priority = 10;

pthread_attr_setschedparam(&attr, &param);

pthread_create(xxx, &attr, xxx, xxx);

pthread_attr_destroy(&attr);

下麵實現一個脫離線程的程序,創建一個線程,其屬性設置為脫離狀態。子線程結束時,要使用pthread_exit,原來的主線程不再等待與子線程重新合並。代碼如下:

view plaincopy to clipboardprint?

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <semaphore.h>
  6. #define SIZE 1024
  7. char buffer[SIZE]; 
  8. void *thread_function(void *arg); 
  9. pthread_mutex_t mutex; 
  10. int main() 
  11. int res; 
  12.     pthread_t a_thread; 
  13. void *thread_result; 
  14.     res = pthread_mutex_init(&mutex, NULL); 
  15. if (res != 0) 
  16.     { 
  17.         perror("Mutex init failed!"); 
  18.         exit(EXIT_FAILURE); 
  19.     } 
  20.     res = pthread_create(&a_thread, NULL, thread_function, NULL); 
  21. if (res != 0) 
  22.     { 
  23.         perror("Thread create failed!"); 
  24.         exit(EXIT_FAILURE); 
  25.     } 
  26.     printf("Input some text. Enter 'end' to finish\n"); 
  27. while (1) 
  28.     { 
  29.         pthread_mutex_lock(&mutex); 
  30.         scanf("%s", buffer); 
  31.         pthread_mutex_unlock(&mutex); 
  32. if (strncmp("end", buffer, 3) == 0) 
  33. break; 
  34.         sleep(1); 
  35.     } 
  36.     res = pthread_join(a_thread, &thread_result); 
  37. if (res != 0) 
  38.     { 
  39.         perror("Thread join failed!"); 
  40.         exit(EXIT_FAILURE); 
  41.     } 
  42.     printf("Thread joined\n"); 
  43.     pthread_mutex_destroy(&mutex); 
  44.     exit(EXIT_SUCCESS); 
  45. void *thread_function(void *arg) 
  46.     sleep(1); 
  47. while (1) 
  48.     { 
  49.         pthread_mutex_lock(&mutex); 
  50.         printf("You input %d characters\n", strlen(buffer)); 
  51.         pthread_mutex_unlock(&mutex); 
  52. if (strncmp("end", buffer, 3) == 0) 
  53. break; 
  54.         sleep(1); 
  55.     } 

編譯這個程序:

gcc -D_REENTRANT thread5.c -o thread5 –lpthread

運行這個程序:

$ ./thread5

thread_function is running. Argument: hello world!

Waiting for thread to finished...

Waiting for thread to finished...

Waiting for thread to finished...

Waiting for thread to finished...

Second thread setting finished flag, and exiting now

Other thread finished!

通過設置線程的屬性,我們還可以控製線程的調試,其方式與設置脫離狀態是一樣的。

7.取消一個線程

有時,我們想讓一個線程可以要求另一個線程終止,線程有方法做到這一點,與信號處理一樣,線程可以在被要求終止時改變其行為。

先來看用於請求一個線程終止的函數:

#include <pthread.h>

int pthread_cancel(pthread_t thread);

這個函數簡單易懂,提供一個線程標識符,我們就可以發送請求來取消它。

線程可以用pthread_setcancelstate設置自己的取消狀態。

#include <pthread.h>

int pthread_setcancelstate(int state, int *oldstate);

參數說明:

state:可以是PTHREAD_CANCEL_ENABLE允許線程接收取消請求,也可以是PTHREAD_CANCEL_DISABLE忽略取消請求。

oldstate:獲取先前的取消狀態。如果對它沒興趣,可以簡單地設置為NULL。如果取消請求被接受了,線程可以進入第二個控製層次,用pthread_setcanceltype設置取消類型。

#include <pthread.h>

int pthread_setcanceltype(int type, int *oldtype);

參數說明:

type:可以取PTHREAD_CANCEL_ASYNCHRONOUS,它將使得在接收到取消請求後立即采取行動;另一個是PTHREAD_CANCEL_DEFERRED,它將使得在接收到取消請求後,一直等待直到線程執行了下述函數之一後才采取行動:pthread_join、pthread_cond_wait、pthread_cond_timedwait、pthread_testcancel、sem_wait或sigwait。

oldtype:允許保存先前的狀態,如果不想知道先前的狀態,可以傳遞NULL。

默認情況下,線程在啟動時的取消狀態為PTHREAD_CANCEL_ENABLE,取消類型是PTHREAD_CANCEL_DEFERRED。

下麵編寫代碼thread6.c,主線程向它創建的線程發送一個取消請求。

view plaincopy to clipboardprint?

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. void *thread_function(void *arg); 
  5. int main() 
  6. int res; 
  7.     pthread_t a_thread; 
  8. void *thread_result; 
  9.     res = pthread_create(&a_thread, NULL, thread_function, NULL); 
  10. if (res != 0) 
  11.     { 
  12.         perror("Thread create failed!"); 
  13.         exit(EXIT_FAILURE); 
  14.     } 
  15.     sleep(4); 
  16.     printf("Canceling thread...\n"); 
  17.     res = pthread_cancel(a_thread); 
  18. if (res != 0) 
  19.     { 
  20.         perror("Thread cancel failed!"); 
  21.         exit(EXIT_FAILURE); 
  22.     } 
  23.     printf ("Waiting for thread to finished...\n"); 
  24.     res = pthread_join(a_thread, &thread_result); 
  25. if (res != 0) 
  26.     { 
  27.         perror ("Thread join failed!"); 
  28.         exit(EXIT_FAILURE); 
  29.     } 
  30.     printf("Thread canceled!"); 
  31.     exit(EXIT_FAILURE); 
  32. void *thread_function(void *arg) 
  33. int i; 
  34. int res; 
  35.     res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 
  36. if (res != 0) 
  37.     { 
  38.         perror("Thread setcancelstate failed!"); 
  39.         exit(EXIT_FAILURE); 
  40.     } 
  41.     res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); 
  42. if (res != 0) 
  43.     { 
  44.         perror("Thread setcanceltype failed!"); 
  45.         exit(EXIT_FAILURE); 
  46.     } 
  47.     printf("thread_function is running...\n"); 
  48. for (i = 0; i < 10; i++) 
  49.     { 
  50.         printf("Thread is still running (%d)...\n", i); 
  51.         sleep(1); 
  52.     } 
  53.     pthread_exit(0); 

編譯這個程序:

gcc -D_REENTRANT thread6.c -o thread6 –lpthread

運行這個程序:

$ ./thread6

thread_function is running...

Thread is still running (0)...

Thread is still running (1)...

Thread is still running (2)...

Thread is still running (3)...

Canceling thread...

Waiting for thread to finished...

8.多線程

之前,我們所編寫的代碼裏麵都僅僅是創建了一個線程,現在我們來演示一下如何創建一個多線程的程序。

view plaincopy to clipboardprint?

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #define NUM 6
  5. void *thread_function(void *arg); 
  6. int main() 
  7. int res; 
  8.     pthread_t a_thread[NUM]; 
  9. void *thread_result; 
  10. int index; 
  11. for (index = 0; index < NUM; ++index) { 
  12.         res = pthread_create(&a_thread[index], NULL, thread_function, (void *)index); 
  13. if (res != 0) 
  14.         { 
  15.             perror("Thread create failed!"); 
  16.             exit(EXIT_FAILURE); 
  17.         } 
  18.         sleep(1); 
  19.     } 
  20.     printf("Waiting for threads to finished...\n"); 
  21. for (index = NUM - 1; index >= 0; --index) 
  22.     { 
  23.         res = pthread_join(a_thread[index], &thread_result); 
  24. if (res == 0) 
  25.         { 
  26.             printf("Picked up a thread:%d\n", index + 1); 
  27.         } 
  28. else
  29.         { 
  30.             perror("pthread_join failed\n"); 
  31.         } 
  32.     } 
  33.     printf("All done\n"); 
  34.     exit(EXIT_SUCCESS); 
  35. void *thread_function(void *arg) 
  36. int my_number = (int)arg; 
  37. int rand_num; 
  38.     printf("thread_function is running. Argument was %d\n", my_number); 
  39.     rand_num = 1 + (int)(9.0 * rand()/(RAND_MAX + 1.0)); 
  40.     sleep(rand_num); 
  41.     printf("Bye from %d\n", my_number); 
  42.     pthread_exit(NULL); 

編譯這個程序:

gcc -D_REENTRANT thread7.c -o thread7 –lpthread

運行這個程序:

$ ./thread7

thread_function is running. Argument was 0

thread_function is running. Argument was 1

thread_function is running. Argument was 2

thread_function is running. Argument was 3

thread_function is running. Argument was 4

Bye from 1

thread_function is running. Argument was 5

Waiting for threads to finished...

Bye from 5

Picked up a thread:6

Bye from 0

Bye from 2

Bye from 3

Bye from 4

Picked up a thread:5

Picked up a thread:4

Picked up a thread:3

Picked up a thread:2

Picked up a thread:1

All done

9.小結

本文主要介紹了Linux環境下的多線程編程,介紹了信號量和互斥量、線程屬性控製、線程同步、線程終止、取消線程及多線程並發。

本文比較簡單,隻作為初學Linux多線程編程入門之用。

最後更新:2017-04-04 07:03:05

  上一篇:go pthread vs openMP之我見
  下一篇:go Android開發20——單個監聽器監聽多個按鈕點擊事件