目錄
0.前言
?1.pthread庫
2.關于控制線程的接口
2.1.創建線程(pthread_create)
2.2.線程等待(pthread_join)
代碼示例1:
?編輯
***一些問題***
2.?3.創建多線程??
3.線程的終止?(pthread_exit? / pthread_cancel)
總結:
4.線程分離 (pthread_detach)
新線程分離主線程
5.C++ 11中的多線程
0.前言
線程的創建,終止,等待,分離
?1.pthread庫
????????Linux中有線程嗎?沒有,只有輕量級進程--(就是線程)。因此Linux下的系統調用只會給用戶提供創建輕量級進程的接口,這些接口需要被pthread庫進行封裝,按照線程的接口提供給用戶,用戶通過這些接口來創建,終止,等待,分離線程。所以我們稱Linux的線程為用戶級線程,windows的線程為內核級線程。
?
2.關于控制線程的接口
2.1.創建線程(pthread_create)
引入接口:pthread_create,用于創建一個新線程
參數說明
pthread_t *thread
:這是一個指向?pthread_t
?類型的指針,用于存儲新創建的線程的標識符。通過這個標識符,你可以引用或操作這個線程。const pthread_attr_t *attr
:這是一個指向?pthread_attr_t
?類型的指針,用于設置線程的屬性,如堆棧大小、調度策略等。如果傳遞?NULL
,則使用默認屬性。void *(*start_routine) (void *)
:這是新線程將要執行的函數的指針。該函數必須接受一個?void *
?類型的參數并返回一個?void *
?類型的值。這個函數的參數?arg
?將被傳遞給新線程。(輸入一個函數的地址)void *arg
:這是傳遞給?start_routine
?函數的參數。如果成功,
pthread_create
?返回?0
;如果失敗,則返回錯誤碼。
2.2.線程等待(pthread_join)
引入接口:pthread_join
參數說明
pthread_t thread
:這是要等待的線程的標識符(ID),該標識符是由?pthread_create
?函數返回的。void **retval
:這是一個指向?void *
?指針的指針,用于接收被等待線程的返回值。如果被等待的線程調用了?pthread_exit
?并傳遞了一個返回值,或者簡單地返回了一個值(對于從?void*
?返回類型的線程函數),那么這個值就可以通過這個參數返回給等待的線程。如果對這個返回值不感興趣,可以傳遞?NULL
。如果成功,
pthread_join
?返回?0
;如果失敗,則返回錯誤碼。
代碼示例1:
線程的創建和等待:
#include <iostream> #include <string> #include <pthread.h> #include <unistd.h>void *threadrun(void *args) {int cnt =10;while(cnt){std::cout<<"new thread run ...,cnt: "<<cnt--<<std::endl;sleep(1);}return nullptr; } int main() {pthread_t tid;int n = pthread_create(&tid,nullptr,threadrun,(void*)"thread 1");std::cout<<"main thread join begin..."<<std::endl;n= pthread_join(tid,nullptr);if(n==0){std::cout<<"main thread wait success"<<std::endl;}return 0; }
***一些問題***
問題1:mian和new線程誰先運行?不確定
問題2:我們期望誰最后退出?main thread,如何來保證呢?
join來保證,不join呢?會造成類似僵尸進程的問題
問題3:tid是什么樣子的?
代碼:以16進制的形式打印出來?
std::string PrintToHex(pthread_t &tid) {char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer; }std::string tid_str = PrintToHex(tid); // 我們按照16進行打印出來std::cout << "tid : " << tid_str << std::endl;
這個線程id是一個虛擬地址,后面再談
問題4:全面看待線程函數傳參,它可以傳任意類型,當然也可以傳類對象的地址,這意味著我們可以給線程傳遞多個參數,多種方法了
class ThreadData { public:std::string name;int num; }; void *threadrun(void *args) {//靜態強轉 ThreadData *td = static_cast<ThreadData*>(args);int cnt =10;while(cnt){std::cout << td->name << " run ..." <<"num is: "<<td->num<< ", cnt: " << cnt-- << std::endl;sleep(1);}return nullptr; } int main() {ThreadData *td=new ThreadData();td->name ="thread-1";td->num = 1;int n = pthread_create(&tid,nullptr,threadrun,(void*)&td); }
傳類對象的時候最好是在堆上開辟,這樣多個線程之間就不會互相干擾。
問題5:pthread_create第三個參數的返回值,該返回值是void*類型的,如果主線程想要獲取線程的返回值,可以通過join函數獲取(在線程沒出錯的情況下是能獲取到的,如果某一個線程出錯,主線程也是會跟著崩掉,因為線程出錯誤,是直接給整個進程發信號的,導致整個進程都掛掉了)
代碼示例:返回一個類對象
#include <iostream> #include <string> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h>class ThreadData { public:int Excute(){return x + y;} public:std::string name;int x;int y;// other };class ThreadResult { public:std::string print(){return std::to_string(x) + "+" + std::to_string(y) + "=" + std::to_string(result);} public:int x;int y;int result; }; std::string PrintToHex(pthread_t &tid) {char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer; } void *threadRun(void *args) {ThreadData *td = static_cast<ThreadData*>(args); // (ThreadData*)argsThreadResult *result = new ThreadResult();int cnt = 10;while(cnt){sleep(3); std::cout << td->name << " run ..." << ", cnt: " << cnt-- << std::endl;result->result = td->Excute();result->x = td->x;result->y = td->y;break;//跑一次退出}delete td;return (void*)result; } int main() {pthread_t tid;ThreadData *td=new ThreadData();td->name="thread-1";td->x=10;td->y=20;int n = pthread_create(&tid, nullptr, threadRun, td);std::string tid_str = PrintToHex(tid); // 我們按照16進行打印出來std::cout << "tid : " << tid_str << std::endl;std::cout<<"main thread join begin..."<<std::endl;ThreadResult *result = nullptr; // 開辟了空間的!!!n = pthread_join(tid, (void**)&result); if(n == 0){std::cout << "main thread wait success, new thread exit code: " << result->print() << std::endl;}sleep(10);return 0; }
2.?3.創建多線程??
下面是一段示例:
初步:創建線程id和線程name,保存所有線程的id信息,最后主線程回收每個線程
#include <iostream> #include <string> #include <vector> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h>const int num = 10; std::string PrintToHex(pthread_t &tid) {char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer; }void *threadrun(void *args) {std::string name = static_cast<const char*>(args);while(true){std::cout << name << " is running" << std::endl;sleep(1);break;}return args; } int main() {std::vector<pthread_t> tids;for(int i = 0; i < num; i++){// 1. 有線程的idpthread_t tid;// 2. 線程的名字char *name = new char[128];snprintf(name, 128, "thread-%d", i+1);pthread_create(&tid, nullptr, threadrun, /*線程的名字*/name);//3.保存所有線程idtids.push_back(tid);}for(auto tid:tids){void*name=nullptr;pthread_join(tid,&name);std::cout<<(const char*)name<<"quit..."<<std::endl;delete (const char*)name;}} ?
我們用vector儲存線程id集
3.線程的終止?(pthread_exit? / pthread_cancel)
????????對于新線程來說,線程終止,函數return;main函數結束,主線程結束,表示整個進程結束!
????????關于exit:專門用來終止進程的,不能用來終止線程!任意一個線程調用exit都表示進程終止!如果你想讓一個線程馬上終止,這里就要用到第三個接口:pthread_exit
參數:
retval
:這是一個指向任意數據的指針,該數據將被線程的終止狀態所使用,并且可以被其他線程通過調用?pthread_join
?來訪問。當然你還可以使用接口:pthread_cancel取消一個線程
參數:
thread
:要發送取消請求的線程標識符(pthread_t 類型)。代碼示例:
#include <iostream> #include <string> #include <vector> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h>const int num = 10; std::string PrintToHex(pthread_t &tid) {char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer; }void *threadrun(void *args) {std::string name = static_cast<const char*>(args);while(true){std::cout << name << " is running" << std::endl;sleep(1);break;}//return args;pthread_exit(args); } int main() {std::vector<pthread_t> tids;for(int i = 0; i < num; i++){// 1. 有線程的idpthread_t tid;// 2. 線程的名字char *name = new char[128];snprintf(name, 128, "thread-%d", i+1);pthread_create(&tid, nullptr, threadrun, /*線程的名字*/name);//3.保存所有線程idtids.push_back(tid);}for(auto tid:tids){void*name=nullptr;pthread_join(tid,&name);std::cout<<(const char*)name<<"quit..."<<std::endl;delete (const char*)name;}sleep(100); }
在主線程未退出的情況下,其它線程成功退出了。
線程取消,退出結果為-1;?#define PTHREAD_CANCELED ((void *) -1)
#include <iostream> #include <string> #include <vector> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h>const int num = 10; std::string PrintToHex(pthread_t &tid) {char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer; }void *threadrun(void *args) {std::string name = static_cast<const char*>(args);while(true){std::cout << name << " is running" << std::endl;sleep(1);} } int main() {std::vector<pthread_t> tids;for(int i = 0; i < num; i++){// 1. 有線程的idpthread_t tid;// 2. 線程的名字char *name = new char[128];snprintf(name, 128, "thread-%d", i+1);pthread_create(&tid, nullptr, threadrun, /*線程的名字*/name);//3.保存所有線程idtids.push_back(tid);}sleep(5);for(auto tid : tids){pthread_cancel(tid); // 取消std::cout << "cancel: " << PrintToHex(tid) << std::endl;void *result = nullptr; // 線程被取消線程的退出結果是:-1 #define PTHREAD_CANCELED ((void *) -1)pthread_join(tid, &result);std::cout << (long long int)result << " quit..." << std::endl;}sleep(100); }
總結:
? ? 新線程如何終止?
? ? 1. 線程函數 return
? ? 2. pthread_exit
? ? 3. main thread call pthread_cancel, 新線程退出結果是-1
4.線程分離 (pthread_detach)
????????線程分離的是將線程與創建它的進程(或主線程)的終止狀態分離。當一個線程被分離后,它依然屬于進程內部,但它不再需要被其他線程顯式地等待(通過?
pthread_join
)來釋放其資源。當分離的線程終止時,它的所有資源會自動被釋放回系統,無需其他線程的干預。
參數
thread
:要分離的線程的標識符(pthread_t 類型)。返回值
- 成功時返回 0。
- 失敗時返回錯誤號。
????????一個線程被創建,默認是joinable,必須要被join的;如果一個線程被分離,線程的工作狀態分離狀態,不需要/不能被join的。
? ? ? ? 這里我們還需要借助一個接口:pthread_self,一調用就是獲取自己的線程id
新線程分離主線程
代碼示例:一旦分離主線程就不能等待了,如果等待會發生什么?這里我們看一下分離且join后,join的返回值
? ? ? ? 我們發現返回值為:22,這說明主線程以等待就直接出錯了。所以主線程無需等待,主線程可以做自己的事情了。如果在線程分離的情況下,且主線程沒有做等待,新線程出錯了,整個進程也是直接掛掉的,因為它還是在進程內部。
#include <iostream> #include <string> #include <vector> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h>const int num = 10; std::string PrintToHex(pthread_t &tid) {char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer; }void *threadrun(void *args) {pthread_detach(pthread_self());std::string name = static_cast<const char*>(args);while(true){std::cout << name << " is running" << std::endl;sleep(1);}pthread_exit(args); } int main() {std::vector<pthread_t> tids;for(int i = 0; i < num; i++){// 1. 有線程的idpthread_t tid;// 2. 線程的名字char *name = new char[128];snprintf(name, 128, "thread-%d", i+1);pthread_create(&tid, nullptr, threadrun, /*線程的名字*/name);//3.保存所有線程idtids.push_back(tid);}sleep(5);for(auto tid : tids){std::cout << "cancel: " << PrintToHex(tid) << std::endl;void *result = nullptr; // 線程被取消線程的退出結果是:-1 #define PTHREAD_CANCELED ((void *) -1)int n = pthread_join(tid, &result);std::cout << (long long int)result << " quit...n :" <<n<< std::endl;}sleep(100); }
5.C++ 11中的多線程
????????C++11在Linux中使用多線程,編譯時也是要鏈接pthread庫,因為C++11中的多線程本質,就是對原生線程庫接口的封裝!!!
#include <iostream> #include <string> #include <vector> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h>void threadrun(std::string name, int num) {while(num){std::cout << name << " num : " << num<< std::endl;num--;sleep(1);} }int main() {std::string name = "thread-1";std::thread mythread(threadrun, std::move(name), 10);while(true){std::cout << "main thhread..." << std::endl;sleep(1);}mythread.join();return 0; }
?
?