wordpress拉寬seo文章優(yōu)化技巧
目錄
?編輯
POSIX線程庫
?多線程創(chuàng)建
獨(dú)立棧結(jié)構(gòu)?
獲取線程ID?
pthread_self?
?線程終止
return終止線程
pthread_exit
pthread_cancel
線程等待
??退出碼問題
?線程分離
測(cè)試?
線程ID及地址空間布局?
?編輯
POSIX線程庫
pthread線程庫是 POSIX線程庫的一部分,POSIX線程庫也叫原生線程庫;
????????遵守 POSIX標(biāo)準(zhǔn):與線程有關(guān)的函數(shù)構(gòu)成了一個(gè)完整的系列,絕大多數(shù)函數(shù)的名字都是以“pthread_”開頭的
要使用這些函數(shù)庫,要通過引入頭文<pthread.h>
鏈接這些線程函數(shù)庫時(shí)要使用編譯器命令的 “-lpthread” 選項(xiàng);成功返回0,失敗返回-1
?多線程創(chuàng)建
主線程創(chuàng)建一批線程;
沒有在循環(huán)創(chuàng)建中添加sleep();
void* handler(void* arg)
{const char* name = (const char*)arg;while(true){cout<<"new thread sucess name:"<<name<<endl;sleep(1);}
}int main()
{pthread_t tid;char namebuff[64];for(int i = 0;i<5;i++) {//格式化snprintf(namebuff,sizeof(namebuff),"%s:%d","thread:",i+1);pthread_create(&tid,nullptr,handler,namebuff); //}while(true){cout << "new thread create success, I am main thread" << endl;sleep(1);}return 0;
}
?觀察發(fā)現(xiàn),線程編號(hào)不是我們預(yù)期的從1.2.3..開始的,而是到了最后一個(gè)線程名字;
而且線程確實(shí)創(chuàng)建出來了;
添加循環(huán)創(chuàng)建時(shí)sleep()函數(shù)?;將數(shù)組地址改為拷貝
//把參數(shù)封成結(jié)構(gòu)體
class ThreadData
{
public:pthread_t tid;char namebuffer[64];
};void* start_routine(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);//static_cast 安全的進(jìn)行強(qiáng)制類型轉(zhuǎn)換,C++11int cnt = 10;while(cnt){cout << "cnt:" << cnt-- << " &cnt:" << &cnt << endl;sleep(1);}delete td;return nullptr;
}int main()
{
#define NUM 10//創(chuàng)建一批線程for(int i = 0; i < NUM; i++){ThreadData* td = new ThreadData();//每次循環(huán)new的都是一個(gè)新對(duì)象snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i+1);//i+1 使線程下標(biāo)從1開始pthread_create(&td->tid, nullptr, start_routine, td);}//主線程while(1){cout << "new thread create success, name:main thread" << endl;sleep(1);}return 0;
}
- 主線程創(chuàng)建新線程太快了,新線程都沒有機(jī)會(huì)運(yùn)行,主線程就把10個(gè)新線程創(chuàng)建完畢了,
- 而傳參namebuffer傳過去的是 緩沖區(qū)namebuffer的起始地址,
- 第十個(gè)線程創(chuàng)建完成之后,緩沖區(qū)的內(nèi)容都被第十個(gè)線程的編號(hào)內(nèi)容覆蓋了,所以第一次現(xiàn)象線程的編號(hào)都是 10
獨(dú)立棧結(jié)構(gòu)?
線程棧主要用于存儲(chǔ)線程的局部變量、函數(shù)參數(shù)以及調(diào)用堆棧。當(dāng)一個(gè)線程開始執(zhí)行時(shí),它的棧空間會(huì)被初始化,并且隨著線程的執(zhí)行,??臻g會(huì)被動(dòng)態(tài)地?cái)U(kuò)展或收縮。
//把參數(shù)封成結(jié)構(gòu)體
class ThreadData
{
public:pthread_t tid;char namebuffer[64];
};void* start_routine(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);//static_cast 安全的進(jìn)行強(qiáng)制類型轉(zhuǎn)換,C++11int cnt = 10;while(cnt){cout << "cnt:" << cnt-- << " &cnt:" << &cnt << endl;sleep(1);}delete td;return nullptr;
}int main()
{
#define NUM 10//創(chuàng)建一批線程for(int i = 0; i < NUM; i++){ThreadData* td = new ThreadData();//每次循環(huán)new的都是一個(gè)新對(duì)象snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i+1);//i+1 使線程下標(biāo)從1開始pthread_create(&td->tid, nullptr, start_routine, td);}//主線程while(1){cout << "new thread create success, name:main thread" << endl;sleep(1);}return 0;
}
在函數(shù)內(nèi)部定義的變量叫局部變量,具有臨時(shí)性,在多線程的情況下依舊適用,因?yàn)槊總€(gè)線程都有自己的獨(dú)立棧結(jié)構(gòu)?
獲取線程ID?
常見獲取線程ID的方式有兩種:
- 創(chuàng)建線程時(shí)通過輸出型參數(shù)獲得
- 通過調(diào)用pthread_self函數(shù)獲得
pthread_self?
?
void* start_routine(void* args)
{//安全轉(zhuǎn)換std::string name = static_cast<const char*>(args);while(true){std::cout<<name<<" running ...,ID: "<<pthread_self()<<std::endl;sleep(1);}
}int main()
{pthread_t thread_id;//創(chuàng)建一個(gè)線程pthread_create(&thread_id,nullptr,start_routine,(void*)"thread 1:");//打印一下主線程的IDwhile(true){std::cout<<"main thread"<<" ID: "<<pthread_self()<<std::endl;sleep(1);}// 等待子線程結(jié)束pthread_join(thread_id, NULL);return 0;
}
?
?線程終止
如果需要只終止某個(gè)線程而不終止整個(gè)進(jìn)程
可以有三種方法:
- 從線程函數(shù)return。這種方法對(duì)主線程不適用,從main函數(shù)return相當(dāng)于調(diào)用exit,整個(gè)進(jìn)程退出
- 線程可以調(diào)用 pthread_ exit 終止自己
- 一個(gè)線程可以調(diào)用 pthread_ cancel 終止同一進(jìn)程中的另一個(gè)線程
return終止線程
在多線程程序中,return關(guān)鍵字的使用有所不同
當(dāng)非主線程時(shí),僅表示該線程將終止其執(zhí)行;在main函數(shù)中使用return則意味著整個(gè)進(jìn)程將退出,這會(huì)導(dǎo)致進(jìn)程的所有資源被釋放。
用例
主線程創(chuàng)建多個(gè)新線程后,休眠2秒,然后進(jìn)行return,那么整個(gè)進(jìn)程也就退出了??
class ThreadData
{
public:pthread_t tid;char namebuffer[64];
};void* start_routine(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);//static_cast 安全的進(jìn)行強(qiáng)制類型轉(zhuǎn)換,C++11int cnt = 10;while(cnt){cout << "new thread create success, name:" << td->namebuffer << " cnt:" << cnt-- << endl;sleep(1);}delete td;return nullptr;
}int main()
{
#define NUM 3//創(chuàng)建一批線程for(int i = 0; i < NUM; i++){ThreadData* td = new ThreadData();snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i+1);//i+1 使線程下標(biāo)從1開始pthread_create(&td->tid, nullptr, start_routine, td);}//主線程cout << "new thread create success, name:main thread" << endl;sleep(2);//主線程兩秒后退出return 0;
}
?如果非主線程執(zhí)行到return,僅代表該線程結(jié)束,線程退出?
pthread_exit
?函數(shù)終止線程?
- exit 是用來終止進(jìn)程的,任何一個(gè)執(zhí)行流調(diào)用 exit,都會(huì)使整個(gè)進(jìn)程退出;
- pthread_exit函數(shù)的功能就是終止線程;
?注意
當(dāng)pthread_exit()和return時(shí),如果返回值是一個(gè)指針,該指針指向的內(nèi)存空間應(yīng)該是全局的或者malloc分配的(new 本質(zhì)也是malloc),防止后面有其它線程通過該指針訪問出錯(cuò);
????????當(dāng)然,為了避免潛在威脅,最好確保返回的指針是指向全局,malloc開辟的。這樣可以確保即使線程退出,其他線程仍然可以安全地訪問這些內(nèi)存。不能在線程函數(shù)的棧上分配,因?yàn)楫?dāng)其它線程得到這個(gè)返回指針時(shí)線程函數(shù)已經(jīng)退出了 ;
pthread_cancel
?函數(shù)取消線程?
// 定義一個(gè)用于存儲(chǔ)線程數(shù)據(jù)的類
class ThreadData
{
public:int number; // 線程編號(hào)pthread_t tid; // 線程IDchar namebuffer[64]; // 緩沖區(qū),用于存儲(chǔ)線程名稱
};// 線程函數(shù)
void* start_routine(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);int cnt = 10;while (cnt > 0){cout << td->namebuffer << " cnt:" << cnt-- << endl;sleep(1);}// 使用 pthread_exit 返回線程編號(hào)pthread_exit((void*)td->number);
}int main()
{vector<ThreadData*> threads; // 用于存儲(chǔ)線程數(shù)據(jù)的向量
#define NUM 5 // 定義要?jiǎng)?chuàng)建的線程數(shù)量for (int i = 0; i < NUM; i++) // 創(chuàng)建一批線程{ThreadData* td = new ThreadData(); // 創(chuàng)建 ThreadData 實(shí)例td->number = i + 1; // 設(shè)置線程編號(hào)snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", td->number);pthread_create(&td->tid, nullptr, start_routine, td);threads.push_back(td); // 把每個(gè)線程的信息 push 到 threads 向量中}// 主線程for (auto& iter : threads){// 輸出創(chuàng)建成功的線程信息cout << "create thread: " << iter->namebuffer << " : " << iter->tid << " success" << endl;}sleep(5); // 主線程休眠5秒// 取消線程for (auto& iter : threads){// 取消線程pthread_cancel(iter->tid);cout << "pthread_cancel: " << iter->namebuffer << endl;}// 等待線程結(jié)束for (auto& iter : threads){void* ret = nullptr;int n = pthread_join(iter->tid, &ret); // 等待線程結(jié)束,并獲取線程返回值// 斷言assert(n == 0);// 輸出線程結(jié)束的信息和線程退出時(shí)返回的值cout << "join: " << iter->namebuffer << " success, thread_exit_code: " << (long long)ret << endl;delete iter; // 釋放 ThreadData 實(shí)例}// 主線程退出cout << "main thread quit" << endl;return 0;
}
?
一個(gè)線程被取消,它的退出碼是 -1
線程等待
線程跟進(jìn)程一樣,創(chuàng)建后也需要主線程等待回收,如果主線程不對(duì)新線程進(jìn)行等待,如果主線程不對(duì)新創(chuàng)建的線程進(jìn)行等待,那么這個(gè)新線程的資源將不會(huì)被及時(shí)回收。這會(huì)導(dǎo)致類似“僵尸進(jìn)程”的問題,也就是內(nèi)存泄漏 ;
- 已經(jīng)退出的線程,其空間沒有被釋放,仍然在進(jìn)程的地址空間內(nèi)。
- 創(chuàng)建新的線程不會(huì)復(fù)用剛才退出線程的地址空間
??退出碼問題
?
- void* retval 和 void** retval 有什么關(guān)系??
- 線程函數(shù)start_routine函數(shù)的返回值類型也是 void*,?start_routine函數(shù)的返回值返回到哪里??
- 我們?cè)趺传@取線程的退出碼,即線程的返回值??
pthread_join函數(shù)的參數(shù) void** retval 是一個(gè)輸出型參數(shù),用來獲取線程函數(shù)結(jié)束時(shí),返回的退出結(jié)果
void** retval 是用來獲取線程函數(shù)返回的退出結(jié)果,因?yàn)榫€程函數(shù)的返回值是 void*,所以需要用 void** 來接受 void*
注意:線程函數(shù)返回的退出結(jié)果是返回在線程庫當(dāng)中,參數(shù) void** retval 需要去線程庫里面接受才可以返回
?線程分離
新創(chuàng)建的線程是 joinable(可以被等待)的。這意味著線程退出后,需要對(duì)其執(zhí)行 pthread_join 操作來釋放資源,否則這些資源將不會(huì)被釋放,從而可能導(dǎo)致系統(tǒng)資源泄漏。如果不關(guān)心線程的返回值的話,線程等待pthread_join是一種負(fù)擔(dān);
可以使用?pthread_detach來分離線程。分離后的線程會(huì)在退出時(shí)自動(dòng)釋放其資源,無需主線程進(jìn)行?pthread_join操作。
?
測(cè)試?
// 將線程ID轉(zhuǎn)換為字符串
string changeID(const pthread_t& thread_id)
{char tid[128];// 將線程ID格式化為十六進(jìn)制字符串snprintf(tid, sizeof(tid), "0x%x", thread_id);return tid;
}// 線程函數(shù)
void* start_routine(void* args)
{string threadname = static_cast<const char*>(args);int cnt = 5;while (cnt > 0){// 輸出線程名稱和線程ID cout << threadname << " running..., threadID:" << changeID(pthread_self()) << endl;sleep(1);cnt--;}// 線程函數(shù)結(jié)束return nullptr;
}int main()
{pthread_t tid;// 創(chuàng)建一個(gè)新線程pthread_create(&tid, nullptr, start_routine, (void*)"thread 1");// 分離線程pthread_detach(tid); // 分離線程后,線程將在退出時(shí)自動(dòng)釋放資源// 線程默認(rèn)是 joinable 的,一旦分離,就不允許再使用 pthread_join// pthread_join(tid, nullptr); // 這里如果嘗試使用 pthread_join 會(huì)引發(fā)錯(cuò)誤// 獲取主線程IDstring mainID = changeID(pthread_self()); // 主線程IDwhile (1){// 輸出主線程的信息 由于新線程已經(jīng)被分離,其ID實(shí)際上已經(jīng)不再有效,只是為了展示。cout << "main running..., mainID:" << mainID << ", new threadID:" << changeID(tid) << endl;sleep(1);}return 0;
}
線程ID及地址空間布局?
pthread_create函數(shù)會(huì)產(chǎn)生一個(gè)線程ID,存放在第一個(gè)參數(shù)指向的地址中。該線程ID和前面說的線程ID不是一回事。
前面講的線程ID屬于進(jìn)程調(diào)度的范疇。因?yàn)榫€程是輕量級(jí)進(jìn)程,是操作系統(tǒng)調(diào)度器的最小單位,所以需要一個(gè)數(shù)值來唯一表示該線程。
pthread_create函數(shù)第一個(gè)參數(shù)指向一個(gè)虛擬內(nèi)存單元,該內(nèi)存單元的地址即為新創(chuàng)建線程的線程ID,屬于NPTL線程庫的范疇。線程庫的后續(xù)操作,就是根據(jù)該線程ID來操作線程的。
用戶級(jí)線程:線程ID值就是庫中結(jié)構(gòu)體(TCB)對(duì)象的地址?