目录
引入
模拟实现
思路
传递参数包
代码
thread.hpp
main.cpp
示例
引入
之前我们一直使用的都是linux中的原生线程库,但c++中其实是有提供封装好的线程库的 -- <thread>
下面我们也来试着封装一下线程接口
模拟实现
思路
首先,明确线程库的核心操作:
- 创建和销毁线程
其次,明确需要的信息(变量):
- 线程id
- 线程执行函数
这里,我们贴近标准库中的定义,使用参数包接收传入的参数
- 所以我们还需要定义一个tuple类型(元组),来存放参数包
- 因为参数包类型之间不能直接赋值,所以我们需要使用元组作为中间媒介
和标准线程库相比,我们还可以添加一些额外信息:
- 线程名
- 线程创建时间
传递参数包
基本难点都在于 -- 如何将接收到的参数包,传递给传入的函数方法
我们这里提供两种解决方法:
- 使用c++14提供的索引序列模板类(index_sequence)作为辅助,然后使用get函数展开参数包
- 使用c++17提供的apply函数,它可以直接将参数包的元素传递给函数
代码
thread.hpp
#include <pthread.h> #include <stdlib.h> #include <string> #include <unistd.h> #include <functional> #include <iostream> template <typename... Args> using callback_t = std::function<void(Args...)>; // 定义一个函数类型,用于接收传入的函数指针 static int NUM = 1; template <class... Args> class thread { public: thread(callback_t<Args...> cb, Args... args) : tid_(0), name_(""), start_timestamp_(0), args_(std::make_tuple(args...)), callback_(cb) // 将参数包展开,创建一个元组 { std::string name = "pthread" + std::to_string(NUM++); name_ = name; start_timestamp_ = time(nullptr); pthread_create(&tid_, nullptr, entry, this); } ~thread() { } void join() { pthread_join(tid_, nullptr); } std::string get_name() { return name_; } uint64_t get_time() { return start_timestamp_; } pthread_t get_id() { return tid_; } private: // 使用索引序列传递 static void *entry(void *args) // 调用存放的函数方法 { thread *it = static_cast<thread *>(args); it->call(std::index_sequence_for<Args...>()); // 根据参数包生成索引序列 return nullptr; } template <std::size_t... Is> void call(std::index_sequence<Is...>) { callback_(std::get<Is>(args_)...); // 将元组中的参数按照索引序列,逐个传递给callback_ } // 使用折叠表达式直接展开 static void *entry(void *args) { thread *it = static_cast<thread *>(args); std::apply(it->callback_, it->args_); return nullptr; } private: pthread_t tid_; callback_t<Args...> callback_; // 存储函数方法 std::tuple<Args...> args_; // 存储参数包 std::string name_; uint64_t start_timestamp_; };
main.cpp
#include "thread.hpp" #include <time.h> using namespace std; void test1(int x) { cout << x << endl; usleep(20); } void test2(string data, int id) { usleep(20); cout << id << ":data is " << data << endl; usleep(20); } int main() { thread<int> t1(test1, 100); thread<string, int> t2(test2, "hello world", 2); t1.join(); t2.join(); cout << t1.get_name() << endl; cout << t2.get_name() << ":" << t2.get_time() << endl; return 0; }
示例
可以看到,我们成功创建出线程,且正确执行了派发的函数任务: