文章目錄
- 一、使用C代碼創建線程
- 示例
- API
- 二、線程的相關知識
- 2.1 線程 與 進程 的關系
- 2.2 使用線程的理由
一、使用C代碼創建線程
使用pthread_create
函數創建線程。
示例
示例:創建一個線程,其作用就是打印線程ID和傳入參數。
//demo1
#include <stdio.h>
#include <pthread.h>void* fun1(void *arg)
{printf("t1:%ld thread is create\n", (unsigned long)pthread_self());//pthread_self()打印線程IDprintf("t1: %d\n", *((int*)arg));//函數執行結束,會默認調用 pthread_exit
}int main()
{//int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);int ret;int param = 100;pthread_t t1;//1.創建線程// 線程屬性 線程函數 線程函數參數ret = pthread_create(&t1, NULL, fun1, (void*)¶m);if(ret == 0){printf("main:create t1 success\n");} else {perror("why t1 fail");}printf("main:%ld\n", (unsigned long)pthread_self());//打印主線程ID//2.等待線程pthread_join(t1, NULL);//等待線程結束,防止進程結束,線程還未執行完畢return 0;
}
API
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// 返回:若成功返回0,否則返回錯誤編號
當pthread_create成功則返回0,函數參數:
- 由
tidp
指向的內存單元被設置為新創建線程的線程ID。 attr
參數用于定制各種不同的線程屬性,暫可以把它設置為NULL
,以創建默認屬性的線程。(工作中也多用NULL
)- 新創建的線程從
start_rtn
(函數指針)函數的地址開始運行,該函數只有一個無類型指針參數arg
。 - 如果需要向
start_rtn
函數傳遞的參數不止一個,那么需要把這些參數放到一個結構中,然后把這個結構的地址作為arg
參數傳入。
pthread_join
函數的作用是等待,效果上看是等待線程的執行過程,只有線程執行完畢,才會繼續執行下去。可以在fun1
函數最后添加while1的死循環來驗證:程序不會退出,因為線程一直卡在循環處。感興趣可以多創建一個t2線程來體驗pthread_join
函數的作用,一個函數中不帶死循環,一個函數中帶死循環,都使用pthread_join
函數進行等待。
二、線程的相關知識
2.1 線程 與 進程 的關系
線程是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條進程指的是一個單一順序的控制流,一個進程中可以并發多個線程,每條線程并發執行不同的任務。線程包含了表示進程內執行環境必須的信息,其中包括進程中表示線程的 線程ID、一組寄存器值、棧、調度優先級和策略、信號屏蔽字、errno常量以及線程私有數據。進程的所有信息對所有線程都是共享的,包括可執行的程序文本、程序的全局變量和堆內存、棧以及文件描述符。在Unix和類Unix操作系統中線程也被稱為輕量級進程(lightweight process),但輕量級進程更多指的是內核線程(kernel thread),而把用戶線程(user thread)稱為線程。
- 進程——資源分配的最小單位
- 線程——程序執行的最小單位
進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其他進程產生影響,而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但沒有獨立的地址空間,一個線程死掉就等于整個進程死掉,所以多進程程序比多線程程序健壯,但切換進程時,資源耗費比較大,效率差一些。但對于要求同時進行且又需要共享變量的并發操作,只能使用線程,不能使用進程。
2.2 使用線程的理由
從上面我們知道了進程與線程的區別,其實這些區別也就是我們使用線程的理由。總的來說就是:進程有獨立的地址空間,線程沒有獨立的地址空間(同一進程內的線程共享進程的地址空間)。
使用多線程的理由之一是和進程相比,它是一種非常“節儉”的多任務處理方式。我們知道,在Linux系統下,啟動一個新的進程必須分配給它獨立的地址空間,建立眾多的數據表來維護它的代碼段、堆棧段和數據段,這是一種"昂貴"的多任務工作方式。而運行于一個進程中的多個線程,它們彼此之間使用相同的地址空間,共享大部分數據,啟動一個線程所花費的空間遠遠小于啟動一個進程所花費的空間,而且,線程間彼此切換所需的時間也遠遠小于進程間切換所需要的時間。據統計,總的說來,一個進程的開銷大約是一個線程開銷的30倍左右,當然,在具體的系統上,這個數據可能會有較大的區別。
使用多線程的理由之二是線程間方便的通信機制。對不同進程來說,它們具有獨立的數據空間,要進行數據的傳遞只能通過通信的方式進行,這種方式不僅費時,而且很不方便。線程則不然,由于同一進程下的線程之間共享數據空間,所以一個線程的數據可以直接為其它線程所用,這不僅快捷,而且方便。當然,數據的共享也帶來其他一些問題,有的變量不能同時被兩個線程所修改,有的子程序中聲明為static的數據更有可能給多線程程序帶來災難性的打擊,這些正是編寫多線程程序時最需要注意的地方。
多線程程序作為一種多任務、并發的工作方式,有以下的優點:
- 提高應用程序響應。這對圖形界面的程序尤其有意義,當一個操作耗時很長時,整個系統都會等待這個操作,此時程序不會響應鍵盤、鼠標、菜單的操作,而使用多線程技術,將耗時長的操作(time
consuming)置于一個新的線程,可以避免這種尷尬的情況。- 使多CPU系統更加有效。操作系統會保證當線程數不大于CPU數目時,不同的線程運行于不同的CPU上。
- 改善程序結構。一個既長又復雜的進程可以考慮分為多個線程,成為幾個獨立或半獨立的運行部分,這樣的程序會利于理解和修改。