本文出自https://shuwoom.com博客,歡迎訪問!
一、進程與線程
(1)線程是進程的一個實體,是CPU調度和分派的基本單位,,它是比進程更小的能獨立運行的基本單位.
(2)進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行並且又要共享某些變量的併發操作,只能用線程,不能用進程。
下圖是多線程的結構:
而進程之間的通信有兩種方式,一種是在兩個進程之間分配一個共享內存區域,另一種方法是通過內核來通信
二、_REENTRANT宏
在一個多線程程序裏,默認情況下,只有一個errno變量供所有的線程共享。在一個線程準備獲取剛纔的錯誤代碼時,該變量很容易被另一個線程中的函數調用所改變。類似的問題還存在於fputs之類的函數中,這些函數通常用一個單獨的全局性區域來緩存輸出數據。
爲解決這個問題,需要使用可重入的例程。可重入代碼可以被多次調用而仍然工作正常。編寫的多線程程序,通過定義宏_REENTRANT來告訴編譯器我們需要可重入功能,這個宏的定義必須出現於程序中的任何#include語句之前。
_REENTRANT爲我們做三件事情:
(1)它會對部分函數重新定義它們的可安全重入的版本,這些函數名字一般不會發生改變,只是會在函數名後面添加_r字符串,如函數名gethostbyname變成gethostbyname_r。
(2)stdio.h中原來以宏的形式實現的一些函數將變成可安全重入函數。
(3)在error.h中定義的變量error現在將成爲一個函數調用,它能夠以一種安全的多線程方式來獲取真正的errno的值。
三、基本函數
(1)pthread_create函數
#include <pthread.h>
int pthread_create ( pthread_t *thread, pthread_attr_t * attr, void* (*start_routine)(void*), void *arg );
返回值:調用成功返回“0”,如果失敗則返回一個錯誤。
第一個參數:進程創建時,會分配一個唯一的PID標識,同樣的,線程創建時,也會用一個指向pthread_t類型的數據類型作爲新線程的標識.
第二個參數:對程序的屬性進行設置
第三個參數:線程將要啓動執行的函數,該函數的返回值和參數都是void指針,這樣就可以傳遞任意類型的指針
第四個參數:傳遞給線程將要執行的函數(第三個參數)的參數
(2)pthread_exit函數
#include <pthread.h>
void pthread_exit (void * retval );
線程在結束時必須調用pthread_exit函數,這與一個進程在結束時要調用exit是同樣的道理。
返回值:返回一個指向某個對象的指針,絕不要用它返回一個指向一個局部變量的指針,因爲局部變量會在線程出現嚴重問題時消失得
無影無蹤。
(3)pthread_join函數
#include <pthread.h>
int pthread_join (pthread_t th, void ** thread_return );
pthread_join相當於進程用來等待子進程的wait函數,它的作用是在線程結束後把它們歸併到一起。
返回值:成功時返回“0”, 失敗時返回一個錯誤代碼
第一個參數:將要等待的線程,它就是pthread_create返回的那個標識符
第二個參數:是一個指針,它指向另外一個指針,而這個指針指向線程的返回值
四、簡單例子
thread.c文件
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void*arg);
/*message是共享的數據*/
char message[] = "Hello World";
int main(){
int res;
pthread_t a_thread;
void *thread_result;
/*NULL表示不修改線程的屬性,a_thread是新線程的標識符,以後的對新線程的引用就使用這個標識符*/
res = pthread_create(&a_thread, NULL, thread_function,(void*)message);
if(res != 0){
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("waiting for thread to finish...\n");
/*等待新線程執行完,然後合併新線程,thread_result是新線程的返回值,
這裏是"Thank you for the CPU time",即pthread_exit的參數內容,這個函數會等到新線程結束後才返回*/
res = pthread_join(a_thread, &thread_result);
if(res != 0){
perror("THread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined, it returned %s\n",(char*)thread_result);
printf("Message is now %s\n", message);
exit(EXIT_SUCCESS);
}
void *thread_function(void* arg){
printf("thread_function is running. Argument was %s\n",(char*)arg);
sleep(3);
strcpy(message, "Bye");
pthread_exit("Thank you for the CPU time");
}
編譯程序:gcc -D_REENTRANT thread.c -lpthread -o thread
運行:./thread
程序調用了pthread_create後,新線程開始執行。就是說,調用成功後,我們就有兩個線程在運行。
原先的老線程將執行pthread_create後的代碼,而新線程就去執行thread_function函數。
一開始,message是“Hello World”,但在新線程裏,message被改成“Bye”。新線程結束後,輸出的message依然是“Bye.”,
因爲message是共享的數據(見上圖多線程)
參考:Linux程序設計 操作系統