重慶模板建站軟件網(wǎng)站收錄服務(wù)
目錄
一.POSIX線程庫
二.線程創(chuàng)建
1.創(chuàng)建線程接口
2.查看線程
3.多線程的健壯性問題
4.線程函數(shù)參數(shù)傳遞
5.線程id和地址空間
三.線程終止
1.pthread_exit
2.pthread_cancel
四.線程等待?
五.線程分離
一.POSIX線程庫
站在內(nèi)核的角度,OS只有輕量級進(jìn)程,沒有線程的概念,但是站在用戶的角度我們只有線程沒有輕量級進(jìn)程的概念。因?yàn)長inux下沒有真正意義上的線程,而是用進(jìn)程模擬的線程,所以Linux不會提供直接創(chuàng)建線程的系統(tǒng)調(diào)用,最多給我們提供創(chuàng)建輕量級進(jìn)程的接口。
所以linux對下對LWP的接口進(jìn)行封裝,對上給用戶提供線程控制的接口——POSIX線程庫,pthread庫,這是任何一個linux系統(tǒng)都會帶的庫,又叫原生線程庫。
POSIX線程:
- 與線程有關(guān)的函數(shù)構(gòu)成了一個完整的系列,絕大多數(shù)函數(shù)的名字都是以“pthread_”打頭的。
- 要使用這些函數(shù)庫,要通過引入頭文<pthread.h>。
- 鏈接這些線程函數(shù)庫時要使用編譯器命令的“-lpthread”選項(xiàng)。
?二.線程創(chuàng)建
1.創(chuàng)建線程接口
功能:創(chuàng)建一個新的線程。
原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
參數(shù):
- thread:返回線程ID。
- attr:設(shè)置線程的屬性,attr為NULL表示使用默認(rèn)屬性。
- start_routine:是個函數(shù)地址,線程啟動后要執(zhí)行的函數(shù),該函數(shù)返回值是void*,參數(shù)是void*。
- arg:傳給線程啟動函數(shù)的參數(shù)。
返回值:
- 成功返回0;失敗返回錯誤碼。
?測試代碼:
#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
using namespace std;void *FuncRun(void *argc)
{while (1){cout << "I am thread,my pid:" << getpid() << endl;sleep(1);}
}int main()
{//線程idpthread_t id;//創(chuàng)建線程pthread_create(&id, NULL, FuncRun, NULL);while (1){cout << "I am main,my pid:" << getpid() << endl;sleep(1);}return 0;
}
測試結(jié)果:
說明:
- 線程沒有父子之分,但是線程有主線程,和新線程的區(qū)分。
- 我們可以看到,主線程pid和新線程的pid是相同的。因?yàn)樗麄儽旧砭褪峭粋€進(jìn)程的一部分。
- 新線程和主線程誰先被調(diào)度取決于調(diào)度器。
?2.查看線程
查看線程使用命令:
ps -aL
?3.多線程的健壯性問題
?測試代碼:
#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>using namespace std;void *FuncRun1(void *argc)
{int count = 0;while (1){count++;cout << "I am thread1,my pid:" << getpid() << endl;if (count == 5){int tmp = count / 0;}sleep(1);}
}
void *FuncRun2(void *argc)
{while (1){cout << "I am thread2,my pid:" << getpid() << endl;sleep(1);}
}int main()
{// 線程idpthread_t id1, id2;// 創(chuàng)建線程pthread_create(&id1, NULL, FuncRun1, NULL);pthread_create(&id2, NULL, FuncRun2, NULL);while (1){cout << "I am main,my pid:" << getpid() << endl;sleep(1);}return 0;
}
測試結(jié)果:
說明:
- 代碼其中有一個線程在第五秒的時候,會出現(xiàn)一個除0的問題。
- 當(dāng)一個線程因?yàn)槟骋粋€錯處而導(dǎo)致線程終止的時候,整個進(jìn)程也都會直接終止。
- 因?yàn)樾盘柺前l(fā)送給進(jìn)程的,最終OS直接對由信號做出的處理動作也是針對進(jìn)程的。
?4.線程函數(shù)參數(shù)傳遞
?我們想通過給線程函數(shù)傳參讓線程執(zhí)行更加復(fù)雜的任務(wù)。
#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>using namespace std;// 任務(wù),計算[1-top]的求和,并將結(jié)果存儲到sum中。
struct task
{task(int top, int num): _thread_name("thread" + to_string(num)), _top(top), _sum(0), _num(num){}string _thread_name; // 線程名字int _top; // 計算數(shù)據(jù)范圍int _sum; // 結(jié)果int _num; // 線程編號
};// 線程函數(shù)
void *FuncRun(void *argc)
{task *t = (task *)argc;for (int i = 1; i <= t->_top; i++){t->_sum += i;}
}int main()
{// 線程idpthread_t id1, id2;// 創(chuàng)建線程task t1(100, 1);task t2(150, 2);pthread_create(&id1, NULL, FuncRun, &t1);pthread_create(&id2, NULL, FuncRun, &t2);// 等待線程計算完再輸出結(jié)果sleep(1);cout << t1._thread_name << ":[1-" << t1._top << "]=" << t1._sum << endl;cout << t2._thread_name << ":[1-" << t2._top << "]=" << t2._sum << endl;return 0;
}
測試結(jié)果:
說明:
- 由于接口的設(shè)計上參數(shù)的類型是void* ,這也就使得我們可以給線程函數(shù)傳遞的參數(shù)是非常豐富的。
5.線程id和地址空間
- pthread_ create 函數(shù)會產(chǎn)生一個線程ID,存放在第一個參數(shù)指向的地址中。該線程ID和前面說的線程ID不是一回事。
- 前面講的線程ID屬于進(jìn)程調(diào)度的范疇。因?yàn)榫€程是輕量級進(jìn)程,是操作系統(tǒng)調(diào)度器的最小單位,所以需要一個數(shù)值來唯一表示該線程。
- pthread_ create 函數(shù)第一個參數(shù)指向一個虛擬內(nèi)存單元,該內(nèi)存單元的地址即為新創(chuàng)建線程的線程ID,屬于NPTL線程庫的范疇。線程庫的后續(xù)操作,就是根據(jù)該線程ID來操作線程的。線程庫NPTL提供了pthread_ self函數(shù),可以獲得線程自身的ID:
pthread_t pthread_self(void);
pthread_t 到底是什么類型呢?取決于實(shí)現(xiàn)。對于Linux目前實(shí)現(xiàn)的NPTL實(shí)現(xiàn)而言,pthread_t類型的線程ID,本質(zhì)就是一個進(jìn)程地址空間上的一個地址。
我們在對線程做操作的時候,根本上是使用線程庫對線程進(jìn)行操作,那么線程庫本質(zhì)就是存在于linux上的動態(tài)庫,在我們使用線程庫的時候,線程庫也會向普通的動態(tài)庫一樣加載到共享區(qū)中,我們使用線程庫方法就是訪問自己的地址空間。
線程庫中需要被管理的線程會有很多,線程庫也必然實(shí)現(xiàn)了線程的數(shù)據(jù)結(jié)構(gòu)——TCB(線程控制塊)。
每個線程都有自己的TCB,和獨(dú)立的上下文數(shù)據(jù),以及線程??臻g。pthread_t 就是指向他們的首地址的一個地址。
?三.線程終止
如果需要只終止某個線程而不終止整個進(jìn)程,可以有三種方法:
- 從線程函數(shù)return。這種方法對主線程不適用,從main函數(shù)return相當(dāng)于調(diào)用exit。
- 線程可以調(diào)用pthread_ exit終止自己。
- 一個線程可以調(diào)用pthread_ cancel終止同一進(jìn)程中的另一個線程。
1.pthread_exit
功能:線程終止.
原型:void pthread_exit(void *value_ptr);
參數(shù):value_ptr:value_ptr不要指向一個局部變量,返回線程結(jié)果。
返回值:無返回值,跟進(jìn)程一樣,線程結(jié)束的時候無法返回到它的調(diào)用者(自身)。
?測試代碼:
#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>using namespace std;void *FuncRun1(void *argc)
{int count = 0;while (1){count++;cout << "I am thread-1-count:" << count << endl;sleep(1);// 三秒后線程1退出if (count == 3){pthread_exit(NULL);}}
}void *FuncRun2(void *argc)
{int count = 0;while (1){count++;cout << "I am thread-2-count:" << count << endl;sleep(1);// 三秒后線程2退出if (count == 3){pthread_exit(NULL);}}
}int main()
{// 線程idpthread_t id1, id2;// 創(chuàng)建線程pthread_create(&id1, NULL, FuncRun1, NULL);pthread_create(&id2, NULL, FuncRun2, NULL);while (1){cout << "I am main,my pid:" << getpid() << endl;sleep(1);}return 0;
}
測試結(jié)果:
說明:
- 線程在退出后,主線程并沒有受到影響,進(jìn)程也有沒受到影響。
- 需要注意,pthread_exit或者return返回的指針?biāo)赶虻膬?nèi)存單元必須是全局的或者是用malloc分配的,不能在線程函數(shù)的棧上分配,因?yàn)楫?dāng)其它線程得到這個返回指針時線程函數(shù)已經(jīng)退出了。
2.pthread_cancel
功能:取消一個執(zhí)行中的線程
原型:int pthread_cancel(pthread_t thread);
參數(shù):thread:線程ID
返回值:成功返回0;失敗返回錯誤碼
?測試代碼:
#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>using namespace std;void *FuncRun1(void *argc)
{int count = 0;while (1){count++;cout << "I am thread-1-count:" << count << endl;sleep(1);}
}
void *FuncRun2(void *argc)
{int count = 0;while (1){count++;cout << "I am thread-2-count:" << count << endl;sleep(1);}
}
int main()
{// 線程idpthread_t id1, id2;// 創(chuàng)建線程pthread_create(&id1, NULL, FuncRun1, NULL);pthread_create(&id2, NULL, FuncRun2, NULL);int count = 0;while (1){count++;sleep(1);if (count == 3){// 三秒后終止線程pthread_cancel(id1);pthread_cancel(id2);}cout << "I am main" << endl;}return 0;
}
測試結(jié)果:
四.線程等待?
為什么需要線程等待?
- 已經(jīng)退出的線程,其空間沒有被釋放,仍然在進(jìn)程的地址空間內(nèi)。
- 創(chuàng)建新的線程不會復(fù)用剛才退出線程的地址空間。
?功能:等待線程結(jié)束。
原型:int pthread_join(pthread_t thread, void **value_ptr);
參數(shù):thread:線程ID。
value_ptr:它指向一個指針,后者指向線程的返回值。
返回值:成功返回0;失敗返回錯誤碼。
調(diào)用該函數(shù)的線程將掛起等待,直到id為thread的線程終止。thread線程以不同的方法終止,通過pthread_join得到的終止?fàn)顟B(tài)是不同的,總結(jié)如下:?
- 如果thread線程通過return返回,value_ ptr所指向的單元里存放的是thread線程函數(shù)的返回值。
- 如果thread線程被別的線程調(diào)用pthread_ cancel異常終掉,value_ ptr所指向的單元里存放的是常數(shù),PTHREAD_ CANCELED。
- 如果thread線程是自己調(diào)用pthread_exit終止的,value_ptr所指向的單元存放的是傳給pthread_exit的參數(shù)。
- 如果對thread線程的終止?fàn)顟B(tài)不感興趣,可以傳NULL給value_ ptr參數(shù)。
?測試代碼:
void *FuncRun1(void *argc)
{int *top = (int *)argc;int *sum = new int;for (int i = 1; i <= *top; i++){*sum += i;}// 線程退出pthread_exit(sum);
}void *FuncRun2(void *argc)
{int *top = (int *)argc;int *sum = new int;for (int i = 1; i <= *top; i++){*sum += i;}// 線程退出return sum;
}void *FuncRun3(void *argc)
{int *top = (int *)argc;int *sum = new int;for (int i = 1; i <= *top; i++){*sum += i;sleep(1);}free(sum);// 線程退出
}int main()
{int top1 = 100;int top2 = 150;int top3 = 200;pthread_t id1;pthread_t id2;pthread_t id3;pthread_create(&id1, NULL, FuncRun1, &top1);pthread_create(&id2, NULL, FuncRun2, &top2);pthread_create(&id3, NULL, FuncRun3, &top3);pthread_cancel(id3);// 接受線程返回數(shù)據(jù)void *ret_ptr1;void *ret_ptr2;void *ret_ptr3;// 等待線程pthread_join(id1, &ret_ptr1);pthread_join(id2, &ret_ptr2);pthread_join(id3, &ret_ptr3);cout << "ret1:" << *((int *)ret_ptr1) << endl;free(ret_ptr1);cout << "ret2:" << *((int *)ret_ptr2) << endl;free(ret_ptr2);if (ret_ptr3 == PTHREAD_CANCELED)cout << "ret3:PTHREAD_CANCELED" << endl;return 0;
}
測試結(jié)果:
五.線程分離
- ?默認(rèn)情況下,新創(chuàng)建的線程是joinable的,線程退出后,需要對其進(jìn)行pthread_join操作,否則無法釋放資源,從而造成系統(tǒng)泄漏。
- 如果不關(guān)心線程的返回值,join是一種負(fù)擔(dān),這個時候,我們可以告訴系統(tǒng),當(dāng)線程退出時,自動釋放線程資源。
int pthread_detach(pthread_t thread);
可以是線程組內(nèi)其他線程對目標(biāo)線程進(jìn)行分離,也可以是線程自己分離:
pthread_detach(pthread_self());
joinable和分離是沖突的,一個線程不能既是joinable又是分離的。
測試代碼:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <pthread.h>
#include <unistd.h>using namespace std;
void *FuncRun(void *argc)
{// 線程分離pthread_detach(pthread_self());int count = 0;while (1){count++;cout << "I am thread-count:" << count << endl;sleep(1);}
}int main()
{pthread_t tid;int n = pthread_create(&tid, NULL, FuncRun, NULL);if (n != 0){cerr << "pthread_create:" << strerror(errno) << endl;}sleep(2);// 線程已經(jīng)分離,再去線程等待,pthread_join會立即報錯。if (pthread_join(tid, NULL) == 0){printf("pthread wait success\n");}else{printf("pthread wait failed\n");}return 0;
}
測試結(jié)果:
?