Linux--線程編程

進程
  系統中程序執行和資源分配的基本單位
  每一個進程有本身的數據段、代碼段和堆棧段
  在進行切換時須要有比較複雜的上下文切換
線程
  減小處理機的空轉時間,支持多處理器以及減小上下文切換開銷, 比建立進程小不少
  進程內獨立的一條運行路線
  處理器調度的最小單元,也稱爲輕量級進程

能夠對進程的內存空間和資源進行訪問,並與同一進程中的其餘線程共享html


線程
  線程相關的執行狀態和存儲變量放在 線程控制表
  一個進程能夠有多個線程,有多個線程控制表及堆棧寄存器,共享一個用戶地址空間
多線程同步問題
  線程共享進程的資源和地址空間
  任何線程對系統資源的操做都會給其餘線程帶來影響

2012040513175742.jpg

線程技術發展
  Linux 2.2內核
    不存在真正意義上的線程
  Linux 2 .4內核
    蠠摭線程個數的限制,容許動態地調整進程數上限
  在Linux 內核2.6以前,進程是最主要的處理調度單元,並沒支持內核線程機制
  Linux 2.6內核
    鳩現共享地址空間的進程機制, 在1996年第一次得到線程的支持
線程技術發展
  爲了改善LinuxThread問題,根據新內核機制從新編寫線程庫, 改善Linux對線程的支持
    ㄠIBM主導的新一代POSIX線程庫(Next Generation POSIX Threads,簡稱爲NGPT)
      –NGPT項目在2002年啓動
      –爲了不出現有多個Linux線程標準,在2003年中止該項目
    ㄠ創攀攙 Hat主導的本地化POSIX線程庫 (Native POSIX Thread Library,簡稱爲NTPL)
      –最先在Red Hat Linux9中被支持
      –如今已經成爲GNU C函數庫的一部分,同時也成爲Linux線程的標準
線程標識
  線程ID
    進程ID在整個系統中是惟一的
    線程ID只在它所屬的進程環境中有效
函數: pthread_self()

2012040513254898.jpg

線程標識
  pthread_t類型一般用結構來表示
  不能把它做爲整數處理
    –Linux使用無符號長整數表示
  爲了移植,使用函數來比較線程ID
函數: pthread_equal()

2012040513305667.jpg

複製代碼
 1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4
5int main(){
6 pthread_t thread_id;
7
8 thread_id=pthread_self(); // 返回調用線程的線程ID
9 printf("Thread ID: %lu.\n",thread_id);
10
11if (pthread_equal(thread_id,pthread_self())) {
12// if (thread_id==0) {
13 printf("Equal!\n");
14 } else {
15 printf("Not equal!\n");
16 }
17return0;
18 }
複製代碼

2012040513395336.jpg



線程編程
  操做用戶空間中的線程
建立線程
  調用該線程函數的入口點
  纏用函數pthread_create(),線程建立後,就開始運行相關的線程函數
2012040514044318.jpg
複製代碼
 1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4
5void *thrd_func(void *arg);
6 pthread_t tid;
7
8int main(){
9// 建立線程tid,且線程函數由thrd_func指向,是thrd_func的入口點,即立刻執行此線程函數
10if (pthread_create(&tid,NULL,thrd_func,NULL)!=0) {
11 printf("Create thread error!\n");
12 exit(1);
13 }
14
15 printf("TID in pthread_create function: %u.\n",tid);
16 printf("Main process: PID: %d,TID: %u.\n",getpid(),pthread_self());
17
18 sleep(1); //race
19
20return0;
21 }
22
23void *thrd_func(void *arg){
24// printf("I am new thread!\n");
25 printf("New process: PID: %d,TID: %u.\n",getpid(),pthread_self()); //why pthread_self
26 printf("New process: PID: %d,TID: %u.\n",getpid(),tid); //why pthread_self
27
28 pthread_exit(NULL); //退出線程
29// return ((void *)0);
30 }
複製代碼

2012040518580628.jpg

退出線程
  在線程函數運行完後,該線程也就退出了
  或使用函數pthread_exit(),這是線程的主動行爲
  不能使用exit()
2012040514044920.jpg

使調用進程終止,全部線程都終止了編程

等待線程多線程

  ㄠ蹵N個進程中的多個線程是共享數據段的,一般在線程退出以後,退出線程所佔用的資源並不會隨着線程的終止而獲得釋放ide

  瀠琀梔爀攀愀攙開樀漀椀渀()函數函數

    相似進程的wait()/waitpid()函數,用於將當前線程掛起來等待線程的結束
    是一個線程阻塞的函數,調用它的線程一直等待到被等待的線程結束爲止
    函數返回時,被等待線程的資源就被收回

2012040518593023.jpg

複製代碼
 1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4
5void *thrd_func1(void *arg);
6void *thrd_func2(void *arg);
7
8int main(){
9 pthread_t tid1,tid2;
10void *tret;
11// 建立線程tid1,線程函數thrd_func1
12if (pthread_create(&tid1,NULL,thrd_func1,NULL)!=0) {
13 printf("Create thread 1 error!\n");
14 exit(1);
15 }
16// 建立線程tid2,線程函數thrd_func2
17if (pthread_create(&tid2,NULL,thrd_func2,NULL)!=0) {
18 printf("Create thread 2 error!\n");
19 exit(1);
20 }
21// 等待線程tid1結束,線程函數返回值放在tret中
22if (pthread_join(tid1,&tret)!=0){
23 printf("Join thread 1 error!\n");
24 exit(1);
25 }
26
27 printf("Thread 1 exit code: %d.\n",(int *)tret);
28// 等待tid2結束,線程函數返回值放在tret中
29if (pthread_join(tid2,&tret)!=0){
30 printf("Join thread 2 error!\n");
31 exit(1);
32 }
33
34 printf("Thread 2 exit code: %d.\n",(int *)tret);
35
36return0;
37 }
38
39void *thrd_func1(void *arg){
40 printf("Thread 1 returning!\n");
41// sleep(3);
42return ((void *)1); // 自動退出線程
43 }
44
45void *thrd_func2(void *arg){
46 printf("Thread 2 exiting!\n");
47 pthread_exit((void *)2); // 線程主動退出,返回(void *)2
48 }
複製代碼

2012040520075677.jpg



取消線程post

  在別的線程中要終止另外一個線程
  瀠琀梔爀攀愀攙開挀愀渀挀攀氀()函數
  被取消的線程能夠設置本身的取消狀態
    –被取消的線程接收到另外一個線程的取消請求以後,是接受仍是忽略這個請求
    –若是接受,是馬上進行終止操做仍是等待某個函數的調用等

2012040519003125.jpg

複製代碼
 1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4
5void *thrd_func1(void *arg);
6void *thrd_func2(void *arg);
7
8 pthread_t tid1,tid2;
9
10int main(){
11// 建立線程tid1,線程函數thrd_func1
12if (pthread_create(&tid1,NULL,thrd_func1,NULL)!=0) {
13 printf("Create thread 1 error!\n");
14 exit(1);
15 }
16// 建立線程tid2,線程函數thrd_func2
17if (pthread_create(&tid2,NULL,thrd_func2,NULL)!=0) {
18 printf("Create thread 2 error!\n");
19 exit(1);
20 }
21// 等待線程tid1退出
22if (pthread_join(tid1,NULL)!=0){
23 printf("Join thread 1 error!\n");
24 exit(1);
25 }else
26 printf("Thread 1 Joined!\n");
27// 等待線程tid2退出
28if (pthread_join(tid2,NULL)!=0){
29 printf("Join thread 2 error!\n");
30 exit(1);
31 }else
32 printf("Thread 2 Joined!\n");
33
34return0;
35 }
36
37void *thrd_func1(void *arg){
38// pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
39 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); // 設置其餘線程能夠cancel掉此線程
40
41while(1) {
42 printf("Thread 1 is running!\n");
43 sleep(1);
44 }
45 pthread_exit((void *)0);
46 }
47
48void *thrd_func2(void *arg){
49 printf("Thread 2 is running!\n");
50 sleep(5);
51if (pthread_cancel(tid1)==0) // 線程tid2向線程tid1發送cancel
52 printf("Send Cancel cmd to Thread 1.\n");
53
54 pthread_exit((void *)0);
55 }
複製代碼

2012040520271135.jpg


複製代碼
 1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4
5#define THREAD_NUM 3
6#define REPEAT_TIMES 5
7#define DELAY 4
8
9void *thrd_func(void *arg);
10
11int main(){
12 pthread_t thread[THREAD_NUM];
13int no;
14void *tret;
15
16 srand((int)time(0)); // 初始化隨機函數發生器
17
18for(no=0;no<THREAD_NUM;no++){
19if (pthread_create(&thread[no],NULL,thrd_func,(void*)no)!=0) { // 建立THREAD_NUM個線程,傳入(void*)no做爲thrd_func的參數
20 printf("Create thread %d error!\n",no);
21 exit(1);
22 } else
23 printf("Create thread %d success!\n",no);
24 }
25
26for(no=0;no<THREAD_NUM;no++){
27if (pthread_join(thread[no],&tret)!=0){ // 等待thread[no]線程結束,線程函數返回值放在tret中
28 printf("Join thread %d error!\n",no);
29 exit(1);
30 }else
31 printf("Join thread %d success!\n",no);
32 }
33
34return0;
35 }
36
37void *thrd_func(void *arg){
38int thrd_num=(void*)arg;
39int delay_time=0;
40int count=0;
41
42 printf("Thread %d is starting.\n",thrd_num);
43for(count=0;count<REPEAT_TIMES;count++) {
44 delay_time=(int)(DELAY*(rand()/(double)RAND_MAX))+1;
45 sleep(delay_time);
46 printf("\tThread %d:job %d delay =%d.\n",thrd_num,count,delay_time);
47 }
48
49 printf("Thread %d is exiting.\n",thrd_num);
50 pthread_exit(NULL);
51 }
複製代碼

2012040521572369.jpg




線程同步與互斥
  線程共享進程的資源和地址空間,對這些資源進行操做時,必須考慮線程間同步與互斥問題
  三種線程同步機制
    鈠斥鎖
    信號量
    慍件變量
  互斥鎖更適合同時可用的資源是唯一的狀況
 信號量更適合同時可用的資源爲多個的狀況

2012041520140290.jpg

2012041520143821.jpg

2012041520150457.jpg



互斥鎖
  用簡單的加鎖方法控制對共享資源的原子操做
  只有兩種狀態: 上鎖、解鎖
可把互斥鎖看做某種意義上的全局變量
  在同一時刻只能有一個線程掌握某個互斥鎖,擁有上鎖狀態的線程可以對共享資源進行操做
  若其餘線程但願上鎖一個已經被上鎖的互斥鎖,則該線程就會掛起,直到上鎖的線程釋放掉互斥鎖爲止
互斥鎖保證讓每一個線程對共享資源按順序進行原子操做



互斥鎖分類
  區別在於其餘未佔有互斥鎖的線程在但願獲得互斥鎖時是否須要阻塞等待
  快速互斥鎖
    調用線程會阻塞直至擁有互斥鎖的線程解鎖爲止
    默認爲快速互斥鎖
  檢錯互斥鎖
    爲快速互斥鎖的非阻塞版本,它會當即返回並返回一個錯誤信息
互斥鎖主要包括下面的基本函數:
  互斥鎖初始化:pthread_mutex_init()
  互斥鎖上鎖:pthread_mutex_lock()
  互斥鎖判斷上鎖:pthread_mutex_trylock()
  互斥鎖解鎖:pthread_mutex_unlock()
  消除互斥鎖:pthread_mutex_destroy()

2012041520261796.jpg

2012041520273373.jpg


ContractedBlock.gif View Code

2012041520535874.jpg

和上一版本的程序差別在於有沒有鎖,有鎖的狀況下,必須等"thread x is exiting."以後其餘線程才能繼續。spa



信號量
  操做系統中所用到的PV原子操做,普遍用於進程或線程間的同步與互斥
    本質上是一個非負的整數計數器,被用來控制對公共資源的訪問
  PV原子操做:對整數計數器信號量sem的操做
     次P操做使sem減一,而一次V操做使sem加一
    進程(或線程)根據信號量的值來判斷是否對公共資源具備訪問權限
  –當信號量sem的值大於等於零時,該進程(或線程)具備公共資源的訪問權限
  –當信號量sem的值小於零時,該進程(或線程)就將阻塞直到信號量sem的值大於等於0爲止
PV操做主要用於線程間的同步和互斥
  互斥,幾個線程只設置一個信號量sem
  同步,會設置多個信號量,安排不一樣初值來實現它們之間的順序執行

2012041521064353.jpg

2012041521071970.jpg


信號量函數
  sem_init() 建立一個信號量,並初始化它
  sem_wait()和sem_trywait(): P操做,在信號量大於零時將信號量的值減一
    區別: 若信號量小於零時,sem_wait()將會阻塞線程,sem_trywait()則會當即返回
  sem_post(): V操做,將信號量的值加一同時發出信號來喚醒等待的線程
  sem_getvalue(): 獲得信號量的值
  sem_destroy(): 刪除信號量

2012041521113687.jpg

2012041521114340.jpg


eg. 同步各線程,執行順序爲逆序。操作系統

ContractedBlock.gif View Code

2012041521275298.jpg

相關文章
相關標籤/搜索