一. pthread库提供的接口函数
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg); //创建一个线程
int pthread_join(pthread_t thread, void **retval);//线程等待
int pthread_detach(pthread_t thread);//线程分离
其中,pthread_create为创建线程,pthread_t *thread 是一个输入输出参数,当成功创建线程后,thread为线程的tid,atrr为线程属性,一般设置为NULL,start_routine为函数指针,其传参类型为void*,arg 为待传入参数。
当主线程程序执行完后,主线程的退出会导致整个进程退出,因此会发生其他线程未执行完任务也随着主线程退出而退出,因此可以用pthread_join和pthread_detach实现线程等待或线程分离,其中retval为输入输出参数,可以记录线程执行完后的返回值
注意到线程创建的回调函数类型必须是void*(void*) 因此,如果我们想传多个参数时必须先定义结构体再传结构体的指针,这在书写代码时造成了一定的不便,例如如果我们希望传输一个学生(id,姓名,班级)等信息传给所创造的线程只能用如下方式:
#include<iostream>
#include<string>
#include<pthread.h>
struct student
{
student(int id,int class_id,std::string name)
:_id(id),_class_id(id),_name(name)
{}
int _id;
int _class_id;
std::string _name;
};//学生结构体
void* thread_callback(void* _student) //回调函数
{
student* st=static_cast<student*>(_student); //类型转换
std::cout<<"name:"<<st->_name<<" id:"<<st->_id<<" class_id:"<<st->_class_id<<std::endl;
//输出姓名,id,班级
return nullptr;
}
int main()
{
student st(1,1,"zzz"); //创建学生变量
pthread_t tid; //创建tid
pthread_create(&tid,nullptr,thread_callback,(void*)&st);//创建线程
pthread_join(tid,nullptr);//线程等待
}
因此,我们希望实现可以封装一个线程类,它可以实现任意个数以及任意类型的参数传递
二. 在C++中封装线程
1. 利用lmbad表达式和可变参数模板绑定回调函数
在C语言中,绑定回调函数只能用函数指针,而在C++11中,可以用function绑定回调函数:
#include<functional>
using func=std::function<void(int)>
其中void(int)为函数类型 void为返回值,int为参数
另外lmbda表达式的类型只和传参和返回类型有关,与捕获类型类型无关,例如:
#include<functional>
int main()
{
double pi=3.1415926;
std::function<int(int)> add=[&pi](int a){return a+pi;};
//add的类型为int(int) 与捕获变量pi(double 类型无关)
}
因此可以通过该特性实现绑定任意参数的回调函数:具体过程如下:
#include<iostream>
#include<functional>
class thread
{
public:
using func=std::function<void(void)>;//用c++定义函数
explicit thread(Fn&& func,Args&...arg)
:_func([&arg...,func](){func(arg...);}) //利用lmbda表达式绑定传入参数和回调函数
{
}
private:
func _func; //函数
};
当调用回调函数时,直接调用_fun()即可完成回调;
2.完成线程封装
#ifndef _THREAD_HPP
#define _THREAD_HPP
#include<iostream>
#include<functional>
#include<pthread.h>
#include<cstdio>
#include<error.h>
#include<cstring>
namespace zwr
{
class thread
{
public:
using func=std::function<void(void)>;//用c++定义函数
//typedef void*(*func)(void*); //用C语言定义函数
template<class Fn,class... Args>
explicit thread(Fn&& func,Args&...arg)
:_func([&arg...,func](){func(arg...);})
//利用lmbda表达式绑定传入参数和回调函数(禁止隐式类型转换)
{
if(!pthread_create(&(_tid),nullptr,func_c,\
static_cast<void*>(this)))
{
std::cerr<<errno<<strerror(errno)<<std::endl;
}
//创建线程
}
template<class Fn,class... Args>
explicit thread(Fn&& func,Args&&...arg)
:_func([arg...,func](){func(arg...);})
//利用lmbda表达式绑定传入参数和回调函数(禁止隐式类型转换)
{
if(pthread_create(&(_tid),nullptr,func_c,\
static_cast<void*>(this)))//创建线程
{
std::cerr<<errno<<strerror(errno)<<std::endl;
}
}
~thread()
{
}
bool detach() //线程分离
{
return !pthread_detach(_tid);
}
void join() //线程等待
{
pthread_join(_tid,nullptr);
_tid=0;
}
private:
static void* func_c(void* pth_args) //线程回调函数
{
thread* _thread_this=static_cast<thread*> (pth_args);
_thread_this->_func();
return nullptr;
}
private:
func _func; //函数
pthread_t _tid; //线程id
};
}
#endif
3.测试
#include<iostream>
#include<string>
#include"thread.hpp"
using namespace zwr;
void thread_callback(int id,int class_id,std::string name) //回调函数
{
std::cout<<"name:"<<name<<" id:"<<id<<" class_id:"<<class_id<<std::endl;
//输出姓名,id,班级
}
int main()
{
thread t(thread_callback,1,1,"zzz");//创建线程并传参
t.join();//线程等待
}
输出结果:
[zwr@hecsx-e774 thread2.0]$ ./thread_test
name:zzz id:1 class_id:1
封装后的线程大大简化了代码