線程概念
典型的UNIX進程可以看成只有一個控制線程,一個進程在某一時刻只能做一件事。有了多個控制線程以後,在程序設計時就可以把進程設計成某一時刻能夠做不知一件事,每個線程都有各自的獨立任務。
每個線程都包含有表示執行環境所必須的信息,其中包括進程中標識線程的線程ID、一組寄存器值、棧、調度優先級和策略、信號和屏蔽字、errno變量以及線程的私有數據。
一個進程所有信息對進程的所有線程都是共享的,包括可執行程序的代碼、程序的全局內存、堆內存、棧以及文件描述符。
線程的基本特點:
1、線程是進程的實體,可以作爲系統獨立的調試和分派基本單位。
2、線程有不同的狀態,系統提供了多種線程控制的原語(控制方法)。比如:創建線程、銷燬線程。
3、線程不擁有自己的資源(唯一擁有的就是自己的棧空間),只擁有從屬於進程的全部資源,所有資源分配都是面向進程的。
4、一個進程中可以有多個線程同時執行,他們可以執行相同的代碼,也可以執行不同的代碼。
5、同一個進程內的線程都在同一地址空間下活動(0-4G),相對於多進程、多線程的系統開銷小,任務切換快。
6、多進程協調工作時需要通信,而多線程間的數據交換不需要依賴類似IPC的特殊通信機制,簡單而高效。
7、每個線程擁有自己獨立的線程ID、寄存器信息、函數棧等。
8、線程之間也存在優先級。
線程標識
就像每個進程有一個ID一樣,每個線程也有一個ID。
進程ID:用pid_t數據類型來表示,是一個非負整數。線程ID:是用pthread_t數據類型來表示,也是一個非負整數。但是可移植的操作系統中,不能把他們作爲整數處理。因此必須用一個函數來對兩個線程ID及進行比較。
#include<pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
線程可以通過調用pthread_self函數獲得自身的線程ID。
pthread_t pthread_self(void);
當線程需要識別以ID作爲標識的數據結構時,以上兩個函數可以一起使用。
線程創建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
thread:獲取線程ID
attr:用於定製各種不同的線程屬性,如果爲NULL按照默認方式創建線程。
start_routine:線程的入口函數地址
arg:給線程入口函數傳遞的參數,如果參數不止一個,需要把這些參數放到結構體中,把結構體的地址作爲arg參數傳入。
線程終止
單個線程可以通過3種方式退出,因此可以在不終止整個進程的情況下,停止它的控制流。
(1)、線程可以從簡單地從啓動歷程返回,返回值是線程的退出碼
(2)、線程可以被同一進程中的其他線程取消
(3)、線程調用pthread_exit。
void pthread_exit(void *retval);
retval參數是一個無類型指針,與傳給啓動歷程的單個參數類似。進程中的其他線程(pthread_join)也可以通過調用pthread_join函數訪問到這個指針。
int pthread_join(pthread_t thread, void **retval);
返回值:成功爲0,失敗返回錯誤編號
可以通過調用pthread_join自動把線程置於分離狀態,這樣資源就可以恢復。如果線程已經處於分離狀態,pthread_join調用就會失敗,返回EINVAL。
當一個線程通過調用pthread_exit退出或者簡單地從啓動歷程返回,進程中的其他線程可以通過pthread_join函數獲得該線程的退出狀態。
如果對線程的返回值不感興趣,那麼可以把retval設置爲NULL。在這種情況下,調用pthread_join函數可以等待指定的線程終止,但並不獲取線程的終止狀態。
注意:pthread_creat和pthread_exit函數的無類型指針參數可以傳遞的值不止一個,這個指針可以傳遞包含複雜信息的結構的地址,但是這個結構所使用的內存在調用者完成調用以後仍然必須是有效的。
線程取消
int pthread_cancel(pthread_t thread);
注意:線程可以選擇忽略取消或者控制如何被取消。pthread_cancel並不等待線程終止,僅僅是提出請求。
int pthread_setcancelstate(int state, int *oldstate);
這個函數用來設置調用者線程是否響應取消操作。
state:
PTHREAD_CANCEL_ENABLE | 允許響應 |
PTHREAD_CANCEL_DISABLE | 取消響應 |
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void *thr_fn1(void* arg)
{
printf("thread1 returning\n");
return ((void*)1);
}
void *thr_fn2(void* arg)
{
printf("thread2 exiting\n");
return ((void*)2);
}
int main()
{
int err;
pthread_t tid1,tid2;
void* tret;
err=pthread_create(&tid1,NULL,thr_fn1,NULL);
if(err!=0)
{
perror("create thread1");
}
err=pthread_create(&tid2,NULL,thr_fn2,NULL);
if(err!=0)
{
perror("create thread2");
}
err=pthread_join(tid1,&tret);
if(err!=0)
{
perror("join false thread1");
}
printf("thread1 exit code %ld\n",(long)tret);
err=pthread_join(tid2,&tret);
if(err!=0)
{
perror("join false thread2");
}
printf("thread2 exit code %ld\n",(long)tret);
exit(0);
}
$./a.out
thread1 returning
thread1 exit code 1
thread2 exiting
thread2 exit code 2
線程同步
臨界區,互斥量,事件對象,信號量
線程屬性
下篇文章中會詳細介紹