提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔
文章目錄
一、為什么需要封裝線程庫?
pthread的痛點:
封裝帶來的好處:
二、線程封裝核心代碼解析
1. 頭文件定義(Thread.hpp)
三、關鍵技術點詳解
1. std::function的魅力
2. 靜態成員函數的巧妙使用
3. 獲取真實的線程ID
四、使用示例和測試代碼
測試代碼(main.cpp)
一、為什么需要封裝線程庫?
在Linux C++開發中,我們經常需要使用多線程。原生的pthread接口雖然強大,但存在一些問題:
pthread的痛點:
-
🔧?C風格接口:函數指針和void*參數不夠類型安全
-
📝?冗長的代碼:需要手動管理線程創建、連接、銷毀
-
🚫?易出錯:容易忘記檢查返回值,導致難以調試的問題
-
🔄?缺乏RAII:資源管理需要手動處理
封裝帶來的好處:
-
🎯?類型安全:使用std::function代替函數指針
-
🚀?簡潔易用:幾行代碼完成線程管理
-
🛡??異常安全:利用RAII自動管理資源
-
📦?可擴展性:方便添加新功能(如線程池)
二、線程封裝核心代碼解析
1. 頭文件定義(Thread.hpp)
?
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <functional>
#include <sys/syscall.h>// 獲取輕量級進程ID(線程ID)
#define get_lwp_id() syscall(SYS_gettid)// 定義函數類型別名
using func_t = std::function<void()>;class Thread
{
public:// 構造函數:接收線程函數和線程名Thread(func_t func, const std::string &name): _name(name), _func(func), _isrunning(false){}// 靜態成員函數:線程啟動例程static void *start_routine(void *args){Thread *self = static_cast<Thread *>(args);self->_isrunning = true;self->_lwpid = get_lwp_id(); // 獲取實際線程IDself->_func(); // 執行用戶函數pthread_exit((void *)0); // 線程退出}// 啟動線程void Start(){int n = pthread_create(&_tid, nullptr, start_routine, this);if (n == 0){std::cout << "線程" << _name << "創建成功" << std::endl;}}// 等待線程結束void Join(){if (!_isrunning) return;int n = pthread_join(_tid, nullptr);if (n == 0){std::cout << "線程" << _name << "回收成功" << std::endl;}}~Thread() {}private:bool _isrunning; // 線程運行狀態pthread_t _tid; // 線程IDpid_t _lwpid; // 輕量級進程IDstd::string _name; // 線程名稱func_t _func; // 線程執行函數
};
三、關鍵技術點詳解
1. std::function的魅力
傳統pthread的問題:
?// C風格:需要靜態函數和void*參數
void* thread_func(void* arg) {// 需要類型轉換int* data = (int*)arg;// ...
}
我們的解決方案:
?
// C++11風格:類型安全的函數對象
using func_t = std::function<void()>;// 可以接收任何可調用對象:
// 1. 普通函數
// 2. Lambda表達式
// 3. 函數對象
// 4. std::bind表達式
2. 靜態成員函數的巧妙使用
為什么需要靜態函數?
pthread_create要求C風格的函數指針,但普通成員函數有隱式的this參數。
解決方案:
?
static void *start_routine(void *args) {Thread *self = static_cast<Thread *>(args); // 轉換回對象指針self->_func(); // 調用真正的線程函數
}
3. 獲取真實的線程ID
?
#define get_lwp_id() syscall(SYS_gettid)// 為什么需要這個?
// - pthread_t是進程內標識,在不同進程中可能重復
// - 通過系統調用獲取的LWP ID是系統范圍內唯一的
// - 便于調試和系統監控
四、使用示例和測試代碼
測試代碼(main.cpp)
?
#include "Thread.hpp"
#include <iostream>
#include <vector>// 測試函數
void Test()
{int cnt = 3;while (cnt--){std::cout << "線程" << get_lwp_id() << "正在運行..." << std::endl;sleep(1);}
}// Lambda表達式測試
auto lambda_test = []() {std::cout << "Lambda線程運行中" << std::endl;sleep(2);
};int main()
{std::cout << "=== 單線程測試 ===" << std::endl;Thread t1(Test, "single-thread");t1.Start();t1.Join();std::cout << "\n=== 多線程測試 ===" << std::endl;std::vector<Thread> threads;// 創建3個線程for (int i = 0; i < 3; i++){std::string name = "thread-";name += std::to_string(i + 1);threads.emplace_back(Test, name);}// 啟動所有線程for (auto &thread : threads){thread.Start();}// 等待所有線程結束for (auto &thread : threads){thread.Join();}std::cout << "\n=== Lambda測試 ===" << std::endl;Thread t2(lambda_test, "lambda-thread");t2.Start();t2.Join();return 0;
}