linux下线程的两种封装方式
在网络编程的时候往往需要对Linux下原生的pthread库中的函数进行封装,使其使用起来更加方便,封装方法一般有两种:面向对象和基于对象,下面将分别介绍这两种方式,最后统一分析这两种方式的优缺点:
面向对象:
面向对象的封装方式是通过虚函数提供回调功能,我们创建一个Thread类,然后设置一个run虚函数,使用时只需要重载这个类并实现其中的虚函数即可:
具体实现如下:
//------thread.h---------------- #ifndef _THREAD_H_
#define _THREAD_H_ #include <pthread.h> class Thread
{
public:
Thread();
virtual ~Thread();
void start();
void join();
void setAutoDelete(bool autoDelete); private:
static void* threadRoutine(void* args);
virtual void run() = ;
pthread_t threadId_;
bool autoDelete_;
}; #endif
//---------thread.c------------------- #include "thread.h"
#include <iostream>
using namespace std; Thread::Thread() : autoDelete_(false)
{
cout << "Thread..." << endl;
} Thread::~Thread()
{
cout << "~Thread..." << endl;
} void* Thread::threadRoutine(void* arg)
{
Thread* thread = static_cast<Thread*>(arg);
thread->run(); if (thread->autoDelete_)
{
delete thread;
} return NULL;
} void Thread::start()
{
pthread_create(&threadId_,NULL, threadRoutine,this);
} void Thread::join()
{
pthread_join(threadId_, NULL);
} void Thread::setAutoDelete(bool autoDelete)
{
autoDelete_ = autoDelete;
}
调用方式如下:
#include "thread.h"
#include <unistd.h>
#include <iostream>
using namespace std; class TestThread : public Thread
{
public:
TestThread(int count) : count_(count)
{
cout << "TestThread..." << endl;
}
~TestThread()
{
cout << "~TestThread..." << endl;
}
private:
void run()
{
while( count_-- )
{
cout << "This is a test" << endl;
sleep();
}
} private:
int count_;
}; int main(void)
{
TestThread t();
t.start();
t.join(); //do other work....
}
可以看到,其中有一个私有的重虚函数run,使用时只需要继承thread,实现run函数,并在其内实现线程需要执行的逻辑就可以了。
同时有一个静态的threadRoutine成员函数,因为C++成员函数缺省的调用方式是__thiscall,成员函数中隐含的第一个参数都是this指针,所以不能匹配给pthread_create的形参void *(*start_routine) (void *), 这时候就可以传递类的一个静态成员,把this指针做为该静态成员的参数。也就是start方法的: pthread_create(&threadId_,NULL, threadRoutine,this); 在threadRoutine函数中将接收到的void*类型的参数转换成Thread*就可以了,然后调用他的run方法。
在main函数中静态创建了一个TestThread变量 t,然后启动线程, 但是如果线程运行结束了,这个变量 t 就失去存在的价值了, 总不能一直让它在哪里占用着栈上空间吧,此时我们就可以动态创建TestThread, 然后设定其autoDelete_属性,这样当该线程执行完毕后就会检查是否需要delete掉这个堆上变量,这就是autoDelete_属性的作用,可以将main函数修改如下:
int main(void)
{
TestThread *t = new TestThread();
t->setAutoDelete(true);
t->start();
t->join(); //do other work.... }
如果你决心只在堆上分配的话,就可以将析构函数设置为私有的,这样就可以只在堆上分配,但是你还得提供一个公有的自定义的析构方法。
基于对象
基于对象的封装方法,实现思路同上,但是它不是通过虚函数的方式实现回调的功能,而是通过函数绑定的方式, boost库中的bind/function可以实现将一个函数转换成另一种函数,就算是成员函数也可以,它相当于C++中的bind1st,bin2nd等函数适配器,在C++11中也实现了bind/function, 关于boost bind/function的使用方法,可以看这里。这里不做过多介绍。
用基于对象的封装方法如下:
//-------thread.h------- #ifndef _THREAD_H_
#define _THREAD_H_ #include <pthread.h>
#include <boost/function.hpp> class Thread
{
public:
typedef boost::function<void ()> ThreadFunc;
explicit Thread(const ThreadFunc& func);
~Thread();
void start();
void join();
void setAutoDelete(bool autoDelete); private:
static void* threadRoutine(void* args);
void run();
bool autoDelete_;
ThreadFunc func_;
pthread_t threadId_; }; #endif
其中在构造函数中需要ThreadFunc对象,这个函数就是在run中调用的实例,可以看下面代码:
//---------thread.cpp----------- #include "thread.h"
#include <iostream>
using namespace std; Thread::Thread(const ThreadFunc& func):autoDelete_(false),func_(func)
{
cout << "Thread..." << endl;
} Thread::~Thread()
{
cout << "~Thread..." << endl;
} void Thread::run()
{
func_();
} void* Thread::threadRoutine(void* arg)
{
Thread* thread = static_cast<Thread*>(arg);
thread->run(); if (thread->autoDelete_)
{
delete thread;
} return NULL;
} void Thread::start()
{
pthread_create(&threadId_,NULL, threadRoutine,this);
} void Thread::join()
{
pthread_join(threadId_, NULL);
} void Thread::setAutoDelete(bool autoDelete)
{
autoDelete_ = autoDelete;
}
在主函数中调用方法也很简单:
#include "thread.h"
#include <unistd.h>
#include <iostream>
#include <boost/bind.hpp>
using namespace std; void ThreadFunc()
{
cout << "ThreadFunc..." << endl;
} void ThreadFunc2(int count)
{
while( count_-- )
{
cout << "This is a test" << endl;
sleep();
}
} class Foo
{
public:
Foo(int count) : count_(count){ } void MemerFunc()
{
while( count_-- )
{
cout << "This is a test" << endl;
sleep();
}
}
private:
int count_; }; int main(void)
{ //Thread *t = new Thread(ThreadFunc); //Thread *t = new Thread(boost::bind(ThreadFunc2,4)); Foo foo();
Thread *t = new Thread(boost::bind(&Foo::MemerFunc,&foo)); t->setAutoDelete(true);
t->start();
t->join(); //do other work.... }
在上面的例子中我们可以绑定不同的对象到Thread的构造函数中,这样就可以实现随着绑定的不同调用不同的函数。
上面就是线程用面向对象和基于对象的不同封装方式,如果你要问,它们孰优孰劣,请挪步这篇精彩的分析。
linux下线程的两种封装方式的更多相关文章
- Linux 下wdcp支持两种安装方式
wdcp支持两种安装方式1 源码编译 此安装比较麻烦和耗时,一般是20分钟至一个小时不等,具体视机器配置情况而定2 RPM包安装 简单快速,下载快的话,几分钟就可以完成源码安装(ssh登录服务器,执行 ...
- [转载]Java线程的两种实现方式
转载:http://baijiahao.baidu.com/s?id=1602265641578157555&wfr=spider&for=pc 前言 线程是程序的一条执行线索,执行路 ...
- java多线程总结一:线程的两种创建方式及优劣比较
1.通过实现Runnable接口线程创建 (1).定义一个类实现Runnable接口,重写接口中的run()方法.在run()方法中加入具体的任务代码或处理逻辑. (2).创建Runnable接口实现 ...
- java多线程总结:线程的两种创建方式及优劣比较
1.通过实现Runnable接口线程创建 (1).定义一个类实现Runnable接口,重写接口中的run()方法.在run()方法中加入具体的任务代码或处理逻辑. (2).创建Runnable接口实现 ...
- java多线程总结一:线程的两种创建方式及比较
1.线程的概念:线程(thread)是指一个任务从头至尾的执行流,线程提供一个运行任务的机制,对于java而言,一个程序中可以并发的执行多个线程,这些线程可以在多处理器系统上同时运行.当程序作为一个应 ...
- linux下ssh的几种验证方式
ssh的认证方式有很多种,大概可以概括为以下几类: 1.pam认证方式 在配置文件/etc/ssh/sshd_config中对应参数:UsePAM 2.密钥认证方式 配置文件/etc/ssh/sshd ...
- Thread -线程的两种创建方式
package cn.learn.thread.Thread; /* 实现Runnable必须重写run()方法,一般推荐用Runnable实现 */ public class RunnableImp ...
- Linux下SVN的三种备份方式
原文链接:http://blog.csdn.net/windone0109/article/details/4040772 (本文例子基于FreeBSD/Linux实现,windows环境请自己做出相 ...
- linux下文件共享的几种常用方式
1. python方式,做一个简单的服务器.默认是开启8000端口. > python -m SimpleHTTPServer 执行命令后,在浏览器上输入该机器IP+8000端口即可 2. sc ...
随机推荐
- 多线程中join()的用法
Thread中,join()方法的作用是调用线程等待该线程完成后,才能继续用下运行. public class TestThread5 { public static void main(String ...
- hadoopmaster主机上传文件出错: put: File /a.txt._COPYING_ could only be replicated to 0 nodes instead of minReplication (=1). There are 3 datanode(s) running and 3 node(s) are excluded in this operation.
刚开始装好hadoop的时候,namenode机上传文件没有错误,今天打开时突然不能上传文件,报错 put: File /a.txt._COPYING_ could only be replicate ...
- c的文件流读取
strtok(数组,分隔符); atof(数组)返回值为转换后的数字; fgets(数组指针,长度,文件句柄); 整整花了两天啊
- 手把手教你树莓派实现简易室内监控系统(C)之BOA服务器的搭建
本篇主要讲利用BOA服务器做室内监控系统的服务器端. 古人云:万事开头靠百度,实在不行就Google.小编也是一步一步的,亲自搭建成功,不能说是万全之策,仅仅是给大家一个参考就满足了. 第一步: 1. ...
- 理解 Git
Git 如何保存文件 其它版本管理系统通常会保存所有文件及其历次提交的差异(diff / revision),通过 merge 原始文件与各阶段的差异就能获取任何版本的状态 而 Git 保存的是每一次 ...
- [Essay] Apache Flink:十分可靠,一分不差
Apache Flink:十分可靠,一分不差 Apache Flink 的提出背景 我们先从较高的抽象层次上总结当前数据处理方面主要遇到的数据集类型(types of datasets)以及在处理数据 ...
- 关于使用srping @RequestParam 容易出错的地方
大家都知道,在spring中的@RequestParam主要用户传递参数用的,具体的解释就是将请求参数去数据映射到功能处理方法的参数上.其中包括三个参数: value:参数名字,即入参的请求参数名字, ...
- java 学习第二天小练习
1.从控制台输入学员王浩3门课程成绩,编写程序实现 ChengJi (1)数学课和英语课的分数之差 (2)3门课的平均分 代码如下: p.p1 { margin: 0.0px 0.0px 0.0px ...
- FMECA分析
FMECA是针对产品所有可能的故障,并根据对故障模式的分析,确定每种故障模式对产品工作的影响,找出单点故障,并按故障模式的严重度及其发生概率确定其危害性.所谓单点故障指的是引起产品故障的,且没有冗余或 ...
- mysql常用基础操作语法(四)--对数据的简单无条件查询及库和表查询【命令行模式】
1.mysql简单的查询:select 字段1,字段2... from tablename; 如果字段那里写一个*,代表查询所有的字段,等同于指定出所有的字段名,因此如果要查询所有字段的数据,一般都 ...