目錄
一 前言
二 線程控制
1. POSIX線程庫(原生線程庫)
2. 創建線程
2.1 pthread_create
?2.2pthread_self()獲取線程id
?3.線程終止
3.1.return 方式
3.2 pthread_exit?
?4 線程等待
三 理解線程tid?
一 前言
? ?在上一篇文章中我們已經學習了線程的概念,線程的創建,并且已經從根本上了解了線程和進程的相同點及不同點。在學習進程時,我們學習了進程的相關概念,進程控制接口,而線程作為更輕量級的進程,其自然也有著控制接口。?
二 線程控制
1. POSIX線程庫(原生線程庫)
- 與線程有關的函數構成了一個完整的系列,絕大多數函數的名字都是以“pthread_”打頭的
- 要使用這些函數庫,要通過引入頭文 件 <pthread.h>
- 鏈接這些線程函數庫時要使用編譯器命令的“-lpthread”選項
2. 創建線程
2.1 pthread_create
功能:創建一個新的線程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void*), void *arg);
- 參數 thread:返回線程ID
- attr:設置線程的屬性,attr為NULL表示使用默認屬性
- start_routine:是個函數地址,線程啟動后要執行的函數
- arg:傳給線程啟動函數的參數
- 返回值:成功返回0;失敗返回錯誤
上一章節我們是創建了一個線程,接下來我們創建多個線程
#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <cstdio>using namespace std;void* start_routine(void* args )
{string name=static_cast<const char*>(args);//安全類型轉換while(true){cout<<"new thread create success, name: "<<name<<endl;sleep(1);}
}int main()
{//1.創建一批線程vector<pthread_t> tids;#define NUM 10for(int i=0;i<NUM;i++){pthread_t tid;char namebuffer[64];snprintf(namebuffer,sizeof namebuffer,"%s: %d","thread",i);//為每個線程設置編號// pthread_t id;//一旦創建成功,就執行上面的執行流pthread_create(&tid,nullptr,start_routine,(void*)namebuffer);}// //2.主執行流while(true){ cout<<"new thread create success, name: main thread"<<endl;sleep(1);}
?測試結果
🍉:從測試結果我們觀察到和我們預想的結果不一樣,接下來我們用下圖解釋
接下來我們對代碼進行一定修改?
void* start_routine(void* args )
{sleep(1);ThreadData* td =static_cast<ThreadData*>(args);//安全類型轉換int cnt=10;while(cnt){cout<<"new thread create success, name: "<<td->namebuffer<<"cnt: "<<cnt--<<endl;sleep(1);}delete td;return nullptr;
}int main()
{//1.創建一批線程vector<ThreadData*> threads;#define NUM 10for(int i=0;i<NUM;i++){
/在這里我們通過new一個對象//ThreadData* td=new ThreadData();
///snprintf(td->namebuffer,sizeof (td->namebuffer),"%s: %d","thread",i+1);//為每個線程設置編號// pthread_t id;//一旦創建成功,就執行上面的執行流
///這里我們將地址td傳給pthread_create/pthread_create(&(td->tid),nullptr,start_routine,td);
保證了每一個執行流有自己獨立的new對象/threads.push_back(td);sleep(1);}}
🚢:start_routine這個函數現在被十個線程執行,這個函數現在?是重入狀態
這個函數是可重入函數嗎?答案是的,因為這個函數并沒有產生二義性。在函數內部定義的變量叫局部變量,具有臨時性。每個線程都有自己獨立的棧結構
?2.2pthread_self()獲取線程id
?該接口的作用是:獲取調用此接口的線程的id,并將id作為返回值。?
?3.線程終止
3.1.return 方式
exit() 能不能用來終止線程呢?答案是不能的,因為exit是終止進程的,任何一個執行流調用exit()都會讓整個進程退出。?接下來我們引入一個接口,用來終止線程。
3.2 pthread_exit?
🙂:我們在講到進程退出的時候,退出是有退出碼和退出信號的,為什么在線程這里線程退出的返回值是void 什么都沒有呢?
因為線程異常退出,也就是進程退出,所以退出信號是進程該關心的事。??
?4 線程等待
線程也是要被等待的,如果不等待,會造成類似僵尸進程的問題----------內存泄漏
線程等待:
1. 獲取線程的退出信息
2.回收新線程對應的PCB等內核資源,防止內存泄漏。
?pthread_join 接口
for(auto& iter:threads)//遍歷threads{ //等待線程int n=pthread_join(iter->tid,nullptr);assert(n==0);cout<<"join: "<<iter->namebuffer<<"success"<<endl;delete iter;}cout<<"main thread quit"<<endl;
測試結果
??接下來我們對pthread_join(pthread_t thread, void **retval)第二個參數進行一下說明。
?
接下來我們對代碼做個簡單改變,讓大家明白第二個參數的使用,pthread?_join(pthread_tthread,void** retval)函數是如何獲取線程函數的返回結果的。
void* start_routine(void* args )
{ThreadData* td =static_cast<ThreadData*>(args);//安全類型轉換int cnt=10;while(cnt){// cout<<"cnt: "<<cnt <<"&cnt"<< &cnt<<endl;// cnt--;// sleep(1);cout<<"new thread create success, name: "<<td->namebuffer<<"cnt: "<<cnt--<<endl;sleep(1);}// delete td;
*******************這是我們的改動return (void*)2;//我們讓每個線程函數返回2
}
for(auto& iter:threads)//遍歷threads{//我們想要獲取線程函數void*類型的返回結果,要設置一個void*變量void* ret=nullptr;//通過取地址&ret,來取到這個返回結果,所以為什么pthread_join()//第二個參數是void** 類型的,因為其是個輸出線參數。int n=pthread_join(iter->tid,&ret);assert(n==0);cout<<"join: "<<iter->namebuffer<<"success: "<<(long long)ret<<endl;delete iter;}
運行結果?
線程控制
創建線程-------->>>>>線程結束----------------->>>>>線程等待
?我們知道對于線程我們為了回收資源不造成內存泄漏,默認情況下都是要進行join的,但是對于我們需要關心線程返回值的情況,必須使用pthread_join()接口函數。如果我們并不關心該線程的返回值,那么其實我們可以不用手動回收線程,可以讓其系統自動回收,這就是線程分離?
pthread_detach()
該接口的作用是?將線程與主線程分離,主線程就不管該分離線程的返回值、退出和資源回收情況?。這個接口一般是線程自己調用或者主線程調用。
三 理解線程tid?
我們在Linux: 線程概念初識-CSDN博客中說過??int n= pthread_create(&tid,nullptr,thread_routine,(void*)"thread_one");tid是個輸出型參數,這個tid的值并不是LWP的值。接下來我們就要對線程的id進行說明,為什么其是一個地址。
🚀:我們在線程概念初識這章節講過,每個線程都有自己獨立的棧結構,這個時候我們會有個疑問?無論有多少個線程,嚴格來說都在一個進程中,而一個進程有一份程序地址空間,也就是說只有一個棧結構,那么為什么說線程都有自己獨立的棧結構呢?