c++学习笔记之基础---类内声明线程函数的调用
近日需要将线程池封装成C++类,类名为Threadpool。在类的成员函数exec_task中调用pthread_create去启动线程执行例程thread_rounter。编译之后报错如下:
spfs_threadpool.cpp: In member function ‘int Threadpool::exec_task(task*)’:
spfs_threadpool.cpp:174: error: argument of type ‘void* (Threadpool::)(void*)’ does not match ‘void* (*)(void*)’
出现类型不匹配的问题。因为pthread_create需要的参数类型为void* (*)(void*),而thread_rounter作为类的成员函数时其类型是void* (Threadpool::)(void*)的成员函数指针。我们知道类的成员函数在经过编译器处理之后,会变成带有this指针参数的全局函数,所以类型注定是不会匹配的。但是如果将thread_rounter声明为static类型,那么编译器会将static形式的函数,转换成不带this指针的全局函数,所以其类型可以与pthread_create需要的参数类型相匹配。但是类的静态成员函数无法访问类的非静态成员,不过这可以通过传递this指针解决这个问题。
综上,我的这个问题可以这个样子解决。
出问题之前的代码:
void *thread_rounter(void *)//线程执行函数
{
//直接访问类的成员
}
exec_task函数中调用:
pthread_create(&tid,NULL,thread_rounter,NULL);//启动线程执行例程
修复这个问题的代码:
static void *thread_rounter(void *tmp)/线程执行函数
{
Threadpool *p=(Threadpool *)tmp;
//通过p指针间接访问类的非静态成员
}
exec_task函数中调用:
pthread_create(&tid,NULL,thread_rounter,(void *)this);//启动线程执行例程
----------------------------------------------------------------------------------------------------------------------
在网上搜索一下还有其他的解决方案,摘录如下,为了以示尊重标明文章来源,感谢原文作者。
方案二:
将线程启动函数声明为模板函数。
摘录自:http://hi.baidu.com/angelevil2006/item/e1806ec30574ff11515058d1
- template <typename TYPE, void (TYPE::*_RunThread)() >
- void* _thread_t(void* param)//线程启动函数,声明为模板函数
- {
- TYPE* This = (TYPE*)param;
- This->_RunThread();
- return NULL;
- }
- class MyClass
- {
- public:
- MyClass();
- void _RunThread();
- private:
- pthread_t tid;
- };
- void MyClass::_RunThread()
- {
- this->DoSomeThing();
- }
- MyClass::MyClass()
- {
- pthread_create(&tid, NULL, _thread_t<MyClass, &MyClass::_RunThread>, this);
- }
- //函数模版不单可以替换类型本身,还能替换类型的成员函数。
- //注意: 1、名称只能是_RunThread,不能在指定模版参数的时候修改;
- // 2、_RunThread只能是public的,除非把_thread_t定义到MyClass的内部。
采取这个方案放入我的项目中:
出问题之前的代码:
void *thread_rounter(void *)//线程执行函数
{
//直接访问类的成员
}
exec_task函数中调用:
pthread_create(&tid,NULL,thread_rounter,NULL);//启动线程执行例程
修复这个问题的代码:
添加public成员函数:
void Run()
{
//该函数替换上述thread_rounter的执行代码
}
thread_rounter修改为全局模板函数:
template <typename TYPE, void (TYPE::*Run)() >
void * thread_rounter(void * param)
{
TYPE *p=(TYPE*)param;
p->Run();
return NULL;
}
exec_task函数中调用:
pthread_create(&tid,NULL,thread_rounter<Threadpool,&Threadpool::Run>,(void *)this);
总结:
解决这个问题的关键在于想方设法使启动函数指针满足void*(*)(void *)类型。
将启动函数改写成static成员函数适用于可以修改类的源代码的情况。
而将启动函数写成模板函数还可以适用于没有类的源代码的情况,自己写一个类,公共继承自原类,添加启动函数为模板函数即可。
花了三个工作日把原来写的一段通信守护进程代码从过程方法改到了 template class,对于 template 的使用和类的派生明白了不少道理。还有个很受启发的一点,就是 C++ 中如何使用类的成员函数作为创建线程的开始函数。
pthread_create 是 POSIX 标准下创建线程的函数,函数原型是:
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void*(*start_routine)(void *), void *arg);
在 C 中,这个函数使用很简单,只要定义一个参数和返回值均为 void * 类型的函数,使用函数名字作为参数即可。就算不完全符合,可以使用 (void *(*)(void *)) 将其强制转换为符合类型检查规格的函数指针。但是,类的非静态成员函数隐含 this 指针作为第一个参数,所以参数完全不可能转化为 void * 类型,而 C++ 的类型检查要比 C 严格许多。由于我原来写的代码是 C 风格的,自然不会出现类型不符的问题,现在将线程开始函数封装到一个模板类中,再创建线程的时候就不能满足需要了。
在试了几种转换无效之后,从网上搜到一种方法:定义线程开始函数为类的静态成员函数(static member function),这样就不隐含 this 指针了,然后将 this 指针从 pthread_create 最后一个参数传给开始函数,在函数中将 void * 类型的 this 指针强制转换为类指针。看来静态成员函数还是有些妙用的。
===================我叫分割线===================
带着我妈和我妹在北京城里转悠了几天,累得不行。主要原因在我,没有考虑到身体因素,连着玩消耗太大,再加上自己也没车没房,倒公交车和住宾馆也要走很远,费时间又费劲。我都快受不了了,别说我妈了。以后再出去旅游,坚决不会再连着转三天以上,要么紧紧张张地玩两天,要么就花时间长点儿,走走歇歇。唉,谁让咱是穷人呢,又有钱又有闲的日子还没过上呢!
今天看了下积压很久的博客订阅,同学里开始写和继续写的人越来越多了。觉得有些文章比较阴沉低迷,因为自己从大三开始心情就老是跌宕起伏,反而在面对这许多次的分别聚首时坦然一些。要不是周熹在散伙饭时候专门招我,也不会哭得那么厉害。散了散了散了吧,没有离别,怎么会有重逢呢?
还是在 Yourui 的博客上看到小恪去新疆的消息,要是当面看到他,肯定会玩笑说发配三千里伊犁充军去了。这在边关待了两年之后,再回来学积分拓扑之类的数学还能看得下去吗?都说是命运无常,旅途坎坷却能看更美风景,只是不知道那关外还是不是大漠孤烟长河落日的大西北?
刚才去吃饭,走在晚间暖暖的懒洋洋的空气中,忽然有点儿秋天的感觉。想起 7 年前爸爸送我到商丘一高上学的情景,日子可真快啊!老了,老了!
http://blog.csdn.net/luo6620378xu/article/details/8521940
c++学习笔记之基础---类内声明线程函数的调用的更多相关文章
- c++学习笔记之基础---类内声明函数后在类外定义的一种方法
在C++的“类”中经常遇到这样的函数, 返回值类型名 类名::函数成员名(参数表){ 函数体.} 双冒号的作用 ::域名解析符!返回值类型名 类名::函数成员名(参数表) { 函数体. } 这个是在类 ...
- Java学习笔记之---基础语法
Java学习笔记之---基础语法 一. Java中的命名规范 (一)包名 由多个单词组成时,所有字母小写(例如:onetwo) (二)类名和接口 由多个单词组成时,所有单词首字母大写(例如:OneTw ...
- MyBatis:学习笔记(1)——基础知识
MyBatis:学习笔记(1)--基础知识 引入MyBatis JDBC编程的问题及解决设想 ☐ 数据库连接使用时创建,不使用时就释放,频繁开启和关闭,造成数据库资源浪费,影响数据库性能. ☐ 使用数 ...
- ELK-6.5.3学习笔记–elk基础环境安装
本文预计阅读时间 13 分钟 文章目录[隐藏] 1,准备工作. 2,安装elasticsearch. 3,安装logstash. 4,安装kibana 以往都是纸上谈兵,毕竟事情也都由部门其他小伙伴承 ...
- Python学习笔记之基础篇(-)python介绍与安装
Python学习笔记之基础篇(-)初识python Python的理念:崇尚优美.清晰.简单,是一个优秀并广泛使用的语言. python的历史: 1989年,为了打发圣诞节假期,作者Guido开始写P ...
- bootstrap学习笔记之基础导航条 http://www.imooc.com/code/3111
基础导航条 在Bootstrap框中,导航条和导航从外观上差别不是太多,但在实际使用中导航条要比导航复杂得多.我们先来看导航条中最基础的一个--基础导航条. 使用方法: 在制作一个基础导航条时,主要分 ...
- C#学习笔记(基础知识回顾)之值传递和引用传递
一:要了解值传递和引用传递,先要知道这两种类型含义,可以参考上一篇 C#学习笔记(基础知识回顾)之值类型和引用类型 二:给方法传递参数分为值传递和引用传递. 2.1在变量通过引用传递给方法时,被调用的 ...
- C#学习笔记(基础知识回顾)之值类型和引用类型
一:C#把数据类型分为值类型和引用类型 1.1:从概念上来看,其区别是值类型直接存储值,而引用类型存储对值的引用. 1.2:这两种类型在内存的不同地方,值类型存储在堆栈中,而引用类型存储在托管对上.存 ...
- mybatis学习笔记之基础复习(3)
mybatis学习笔记之基础复习(3) mybatis是什么? mybatis是一个持久层框架,mybatis是一个不完全的ORM框架.sql语句需要程序员自己编写, 但是mybatis也是有映射(输 ...
随机推荐
- SQL语句操作SQL SERVER数据库登录名、用户及权限
要想成功访问 SQL Server 数据库中的数据, 我们需要两个方面的授权: 获得准许连接 SQL Server 服务器的权利: 获得访问特定数据库中数据的权利(select, update, de ...
- 5. COLUMNS
5. COLUMNS 表COLUMNS提供表中列的信息. INFORMATION_SCHEMA Name SHOW Name Remarks TABLE_CATALOG TABLE_SCHEMA TA ...
- NFS共享存储服务部署
第1章 NFS介绍 1.1 NFS基本概述 NFS(Network File System)网络文件系统 主要功能是通过局域网络让不同的主机系统之间可以共享文件或目录. NFS系统和Windows网络 ...
- Python旅途——数据类型
1.概要 今天跟大家分享的是数据类型,数据类型在Python中的重要程度就好比我们汉语中的拼音一样重要. 2.数据类型 1.整形 整形就像是数学中的1.2.3一样,用int来表示. a = 1 # ...
- 访问变量的每个字节内容(c语言)
#include <stdio.h> #define fun(x) for(int fun_i = 0; fun_i < sizeof(x); fun_i++){printf(&qu ...
- C#基于引用创建单链表
在C语言,单链表的实现依赖指针,指针用来指向节点,那么,用C#实现,自然就想到引用,节点的引用不就类似于指向Node的指针嘛
- Hadoop JVM调整解决 MapReduce 作业超时问题
摘要:由于业务需要,在mapreduce汇总时需要关联两个基础表,一个60M左右,不影响mr运行,另一个表,大小约为380M,行数为1700万行左右,在默认配置下,一旦加载这个数据就会在reduce阶 ...
- ES6__数据结构 Set
/* 数据结构 Set */ /* *集合的基本概念:集合是由一组无序且唯一(即不能重复)的项组成的.这个数据结构使用了与有限集合相同的数学概念,应用在计算机的数据结构中. *特点:key 和 val ...
- Mysql相关工具
•Mysql相关工具 –Mysqlslap 压力测试工具 –Mysqlsla 日志分析工具 –Mysqlreport 效能监控工具 –Mysqlproxy 快速实现读写分离以及负载均衡 –p ...
- 一段曲折的copy路程
cp 的时候出现:-bash: /bin/cp: Argument list too longcp ./*.swf /www/img/html/xxx/action/ 解决办法:find ./ -n ...