Linux C進程與多線程入門—(4)簡單多線程程序

本文出自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程序設計   操作系統

 

發佈了66 篇原創文章 · 獲贊 82 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章