线程函数为静态函数

内联函数online的定义和作用 说明: 内联函数是一个小型函数,以牺牲空间来节省函数调用带来的额外开销,一般用以比较小函数或者使用频率很高的函数,函数内部内部没有复杂的语句,如开关语句switch,循环语句for等 定义: (1)在 C++ 中,可以在定义函数时,在返回值类型前面加上 inline 关键字 inline int max() { ...... return 0; } (二)直接在类的内部进行函数的声明和定义,则默认其是内联函数 class A { public: int max() { ...... ret 阅读详情

 C++中 线程函数为静态函数 及 类成员函数作为回调函数

 线程函数为静态函数:

  线程控制函数和是不是静态函数没关系,静态函数是在构造中分配的地址空间,只有在析构时才释放也就是全局的东西,不管线程是否运行,静态函数的地址是不变的,并不在线程堆栈中static只是起了一个装饰的作用,所以二者并没有必然的关系

  线程也是一种资源,是受系统调度的。因此,你必须要让系统知道你的线程的起始位置,即线程函数的指针。Window系统的接口(API)是C标准的,系统在启动线程时,需要直接得到线程的起始位置,因此你也必须向系统直接传递这样一个直接的函数指针,这与C++其它的特性是无关的。

  因为对于非静态成员函数而言,默认情况下,参数列表中都会有一个this指针,例如fun(自定义参数),实际上编译后就变成这样类型:fun(自定义参数,某个类 * this)。这样编译就会出错,多了一个参数,所以这个函数就不能作为线程函数了void* fun(void*)。加上static修饰之后,类的成员函数就不会加上默认this了,所以符合调用规定。

  在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static !在C语言中,我们使用pthread_create创建线程,线程函数是一个全局函数,所以在C++中,创建线程时,也应该使用一个全局函数。static定义的类的成员函数就是一个全局函数。

类成员函数作为回调函数

  回调函数是基于C编程的Windows SDK的技术,不是针对C++的,程序员可以将一个C函数直接作为回调函数,但是如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过。 

  普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递一个指向自身的指针给其成员函数从而实现程序函数可以访问C++的数据成员。这也可以理解为什么C++类的多个实例可以共享成员函数但是确有不同的数据成员。由于this指针的作用,使得将一个CALLBACK型的成员函数作为回调函数安装时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数安装失败。

  这样从理论上讲,C++类的成员函数是不能当作回调函数的。但我们在用C++编程时总希望在类内实现其功能,即要保持封装性,如果把回调函数写作普通函数有诸多不便。经过网上搜索和自己研究,发现了几种巧妙的方法,可以使得类成员函数当作回调函数使用。

  这里采用Linux C++中线程创建函数pthread_create举例,其原型如下:

   int pthread_create( pthread_t *restrict tidp , const pthread_attr_t *restrict attr , void* (*start_rtn)(void*) , void *restrict arg );  

  第一个参数为指向线程标识符的指针。

  第二个参数用来设置线程属性。

  第三个参数是线程运行函数的起始地址,即回调函数。

  最后一个参数是运行函数的参数。

  这里我们只关注第三个参数start_run,它是一个函数指针,指向一个以void*为参数,返回值为void*的函数,这个函数被当作线程的回调函数使用,线程启动后便会执行该函数的代码。

 方法一:回调函数为普通函数,但在函数体内执行成员函数

class MyClass  
{  
    pthread_t TID;  
public:  
    void func()  
    {  
        //子线程执行代码  
    }  
  
    bool startThread()  
    {//启动子线程  
        int ret = pthread_create( &TID , NULL , callback , this );  
        if( ret != 0 )  
            return false;  
        else  
            return true;  
    }  
};  
  
static void* callback( void* arg )  
{//回调函数  
    ((MyClass*)arg)->func();调用成员函数  
    return NULL;  
}  
  
int main()  
{  
    MyClass a;  
    a.startThread();  
}  

  类MyClass需要在自己内部开辟一个子线程来执行成员函数func()中的代码,子线程通过调用startThread()成员函数来启动。这里将回调函数callback写在了类外面,传递的参数是一个指向MyClass对象的指针(在pthrad_create()中由第4个参数this指定),回调函数经过强制转换把void*变为MyClass*,然后再调用arg->func()执行子线程的代码。

  这样做的原理是把当前对象的指针当作参数先交给一个外部函数,再由外部函数调用类成员函数,以外部函数作为回调函数,但执行的是成员函数的功能,这样相当于在中间作了一层转换。缺点是回调函数在类外,影响了封装性,这里把callback()限定为static,防止在其它文件中调用此函数。

方法二:回调函数为类内静态成员函数,在其内部调用成员函数

在方法一上稍作更改,把回调函数搬到类MyClass里,这样就保持了封装性。代码如下:

class MyClass  
{  
    static MyClass* CurMy;//存储回调函数调用的对象  
    static void* callback(void*);//回调函数  
    pthread_t TID;  
    void func()  
    {  
        //子线程执行代码  
    }  
      
    void setCurMy()  
    {//设置当前对象为回调函数调用的对象  
        CurMy = this;  
    }  
public:  
    bool startThread()  
    {//启动子线程  
        setCurMy();  
        int ret = pthread_create( &TID , NULL , MyClass::callback , NULL );  
        if( ret != 0 )  
            return false;  
        else  
            return true;  
    }  
};  
MyClass* MyClass::CurMy = NULL;  
void* MyClass::callback(void*)  
{  
    CurMy->func();  
    return NULL;  
}  
  
int main()  
{  
    MyClass a;  
    a.startThread();  
}  

  类MyClass有了1个静态数据成员CurMy和1个静态成员函数callback。CurMy用来存储一个对象的指针,充当方法一中回调函数的参数arg。callback当作回调函数,执行CurMy->func()的代码。每次建立线程前先要调用setCurMy()来让CurMy指向当前自己。

  这个方法的好处时封装性得到了很好的保护,MyClass对外只公开一个接口startThread(),子线程代码和回调函数都被设为私有,外界不可见。另外没有占用callback的参数,可以从外界传递参数进来。但每个对象启动子线程前一定要注意先调用setCurMy()让CurMy正确的指向自身,否则将为其它对象开启线程,这样很引发很严重的后果。

方法三:对成员函数进行强制转换,当作回调函数

class MyClass  
{  
    pthread_t TID;  
    void func()  
    {  
        //子线程执行代码  
    }  
public:  
    bool startThread()  
    {//启动子线程  
        typedef void* (*FUNC)(void*);//定义FUNC类型是一个指向函数的指针,该函数参数为void*,返回值为void*  
        FUNC callback = (FUNC)&MyClass::func;//强制转换func()的类型  
        int ret = pthread_create( &TID , NULL , callback , this );  
        if( ret != 0 )  
            return false;  
        else  
            return true;  
    }  
};  
  
int main()  
{  
    MyClass a;  
    a.startThread();  
}  

  这个方法是原理是,MyClass::func最终会转化成 void func(MyClass *this); 也就是说在原第一个参数前插入指向对象本身的this指针。可以利用这个特性写一个非静态类成员方法来直接作为线程回调函数。对编译器而言,void (MyClass::*FUNC1)()和void* (*FUNC)(void*)这两种函数指针虽然看上去很不一样,但他们的最终形式是相同的,因此就可以把成员函数指针强制转换成普通函数的指针来当作回调函数。在建立线程时要把当前对象的指针this当作参数传给回调函数(成员函数func),这样才能知道线程是针对哪个对象建立的。

  方法三的封装性比方法二更好,因为不涉及多个对象共用一个静态成员的问题,每个对象可以独立地启动自己的线程而不影响其它对象。

使用类的静态成员函数作为线程执行体 使用类的静态成员函数作为线程执行体 工作中读到使用类的静态成员函数作为函数执行体的业务代码,现简单的记录一下; 其中类A是业务类,包含业务处理类B,线程类对象,类A是要获取实时的数据,通过类内创建的新线程将实时数据传递给业务处理类B,同时将类A对象作为函数参数;可以在类A定义线程同步的锁,保证线程间数据的同步; #include <iostream> #include <functional> #include <thread> #include <unistd.h 阅读详情

相关推荐

C++中创建类的非静态函数的线程

在《MFC中类的非静态函数作为工作者线程函数的方法》中,讲解了通过类模板和函数模板创建类的非静态函数的线程的方法。本文将继续介绍创建类的非静态函数线程的另外两种方法。 1 通过全局函数创建 在《MFC中类的非静态函数作为工作者线程函数的方法》中提到,线程函数可以是类的静态成员函数,也可以是全局函数。 1.1 全局函数的定义 定义一个全局函数Func_Global_Thread()作为线程函

hou09tian的博客 1615

C++ thread用法总结(整理)

1,简介 C++11中加入了&lt;thread&gt;头文件,此头文件主要声明了std::thread线程类。C++11的标准类std::thread对线程进行了封装,定义了C++11标准中的一些表示线程的类、用于互斥访问的类与方法等。应用C++11中的std::thread便于多线程程序的移值。 std::thread类成员函数: (1)、get_id:获取线程ID,返回一个类型为std...

sevenjoin的博客 10万+

【Godot】构建可维护的游戏工程 —— 项目代码组织结构

本文系统梳理了Godot引擎项目组织原则与架构模式,提出场景中心化、命名规范一致性和资源隔离三大核心原则。推荐按功能模块划分目录结构(如scenes/、entities/、systems/),采用snake_case命名避免跨平台问题,并通过.gdignore优化资源管理。文章详细解析了自包含场景设计、信号驱动解耦等架构技巧,以及团队协作中的版本控制策略,强调通过模块化设计平衡一致性与实用性。最终目标构建清晰可维护的项目结构,使资源定位和功能扩展更加高效。(148字)

qq_40205510的博客 1176

c++多线程编程--thread成员函数

thread成员函数 thread类提供了丰富的成员函数帮助用户实现多线程编程。我们把构造函数单独拎出来作为一章来介绍。如下 thread构造函数 非构造函数的成员函数介绍: 序号 函数格式 返回值 意义 1 join() void 让当前线程等待线程t执行完成 2 joinable() bool 判断当前线程是否可以join 3 detach() void 继续执行当前线程,不必等待线程t执行完成 4 get_id() std::thread::id 获取线程id 5 st

大海中的游鱼 1694

告别虚拟机!用ZYNQ7000和PYNQ 2.6.0打造一个能实时识别人脸的“智能摄像头”

本文详细介绍了如何利用Xilinx ZYNQ7000开发板和PYNQ 2.6.0框架构建实时人脸识别系统。通过硬件加速和嵌入式开发技术,该系统实现了低延迟、低功耗的独立运行能力,适用于边缘计算和物联网应用。文章包含从环境搭建到核心功能实现的完整指南,并提供了性能优化和问题排查的实用技巧。

weixin_42543046的博客 391

pwscf与wannier90 Hands-On实战训练(一)——费米面计算为例

系列文章目录 Quantum Espresso Hands-On实战训练(三)——DOS计算 文章目录系列文章目录计算过程pwscf直接得到等能面pwscf和wannier90得到等能面pwscf直接得到等能面1. scf2. nscf3. 后处理pwscf和wannier90得到等能面1. Kpoints2. scf3. nscf4. pw2wan及wannier90.x5. XCrySDen可视化 计算过程 pwscf直接得到等能面 pwscf和wannier90得到等能面 pwscf直接得到等能面

weixin_45973682的博客 5070

C++类中的线程处理函数必须为static

C++类中的线程处理函数必须为static,这使得线程处理函数不能使用类中的非static成员变量。线程处理函数之所以需要是static是因为:设某函数原型为LRESULT ThreadProc(LPVOID pv);若为非静态成员函数,编译时自动展开为 ThreadProc(pClass-> this, pv);与线程函数调用不相符。所以必须使用全局函数...

尔雅慕客的博客 1098

LAMMPS实战指南 - 从in文件到分子动力学模拟的完整流程解析

本文详细解析了LAMMPS分子动力学模拟的完整流程,从理解in文件的核心作用与基础结构开始,逐步指导如何创建模拟体系、选择势函数、进行能量最小化与初始化,并深入讲解了NVT/NPT系综弛豫与生产模拟的关键步骤。文章还提供了常见问题的避坑指南与实战技巧,帮助初学者快速掌握从编写in文件到成功运行模拟的全过程。

weixin_28365523的博客 395

C++ 静态成员函数 静态线程函数 非静态成员函数

一、静态线程函数与类的非静态函数 今天写程序要用到一个计时器,然后需要创建一个新的线程,这个线程是CtestDLG的一个成员函数,必须定义为静态的.但是这个时候就不能去访问非静态成员变量了,这里找到一个很好的解决办法: 先上代码: 1.线程函数的定义: private: int Count_time; CTime C_time; bool GetQuestion(...

Gavinli_lgf的博客 1505

将Python爬取的数据导入钉钉多维表

摘要:本文介绍将Python爬取的数据导入钉钉多维表的完整解决方案。首先通过requests库爬取数据并解析为结构化格式,然后使用钉钉开放平台API对接多维表。关键步骤包括获取app_key、app_secret等凭证,通过OAuth2获取access_token,以及批量写入记录。文章详细说明了参数获取位置、字段类型匹配、权限配置等注意事项,并提供了错误处理建议和性能优化方法。适用于需要将网络数据同步到钉钉多维表的开发场景。

IT深耕十余载,大道之简 2131

中国传媒大学计算机考研初复试经验贴

中传的计算机专业考研的话主要分为两大学院:计网学院和国重,大部分专业在计网学院。

DoKiDoK的博客 1561

为什么类中的线程函数必须要声明静态?

其实类的静态函数就跟全局函数是一个样子的, 只是调用的时候要加下个类修饰符而已. 至于为什么不能是非静态成员函数呢, 因为非静态成员函数都会在参数列表中加上一个this指针为为参数, 这样的话你写的线程函数就不符合调用规定了. 比如 DWORD WINAPI ThreadFun(LPVOID); 是非静态的,实际编译后,就会变成 DWORD WINAPI ThreadFun(LPVOID, CMy...

qq_38635138的博客 2201

【GPIO模拟串口通信协议】

由于这段时间搞了个安卓相关的项目,需要和安卓进行Uart通信。但是选用的芯片并没有自带uart功能,所以在充分评估项目后,考虑波特率不高的情况下,决定使用GPIO去模拟串口通信协议了。

学海无涯苦作舟 2638

用非静态类成员函数作为线程函数

非静态的类成员函数是不能直接作为线程函数的,如果把成员函数声明为静态,可以解决问题,但是静态函数只能访问类的静态成员,这又带来很多麻烦。不过可以通过包装解决此问题,如下:class test{    private:          void func();//实际需要作为线程函数的函数    public:          static unsinged in

小辉的专栏 2164

openCv +3.4.0 车牌定位(C++版本)

#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc.hpp> #include "opencv2/shape.hpp" using namespace ...

Bamboo265925的博客 2337

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

创建线程函数,设置静态线程函数

创建线程函数 uintptr_t _beginthreadex( void *security, //线程的安全属性 unsigned stack_size,//线程大小 unsigned ( __stdcall *start_address )( void * ),//线程函数 void *arglist,//线程参数 unsigned initflag,//标识,设置为0,标识立即运行 uns...

冯同学的博客 536

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

非静态成员函数作为线程函数

如题,下面是代码. 1 // header.h 2 class A 3 { 4 public: 5 bool StartServer(int port); 6 7 private: 8 unsigned int __stdcall listenThreadFunction(); 9 10 int mData; ...

aibulie0537的博客 281

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

非静态类成员作为线程函数

Win32API下,非静态的类成员是不能作为线程函数的,主要原因是this指针. 有些情况必须使用线程,例如主线程给用户一个友好的界面工作线程进行网络操作. 当你必须要新建线程执行类成员的时候就不得不妥协使用静态类成员,全局函数或友元.这样的类非常丑. 网上有些解决方案,例如: 把C++类成员方法直接作为线程回调函数> 我的用的方法不一定比上面的方法好,不过我比较喜欢. 我的办法是

chenguangxing3的专栏 1713

多线程静态同步函数

多线程静态同步函数和非静态同步函数的区别是啥? 静态同步函数:就是方法使用了static关键字修饰 非静态同步函数:没有用static关键字修饰的方法,也就是使用this锁的同步函数 那么静态同步函数使用的啥锁呢? 分析:两个线程,一个线程使用静态同步函数,一个使用同步代码块this锁,如果线程不同步(线程不安全),就说明静态同步函数使用的不是this锁,如果线程同步,就说明使用的是this锁。 ...

qq_35657661的博客 387

多线程编程复习 windows线程函数必须为全局函数或者静态函数

调用CreateThread(...)创建线程时要指定所创建线程的入口函数,此入口函数只能是全局函数或者类的静态成员函数。 全局函数很容易理解,但如果是类的成员函数则必须是静态成员函数,为何, 因为类的非静态成员函数只能通过类的对象去调用,但是创建线程时从那里能获得类的对象而去调用类的成员函数呢? 类的静态成员函数类所有,不专属于类的任何一个对象,所以不创建类的对象也可以调用。 全局函

CppPlayer 3041

整理 用类的静态函数作为线程函数 和 用类的非静态函数作为线程函数

整理 用类的静态函数作为线程函数 和 用类的非静态函数作为线程函数

u012219045的专栏 805

静态函数之多线程

静态成员变量可以再非静态成员函数中使用,Bjarne Stroustrup为什么再创建C++的时候要创建一种静态成员函数呢? C++产生年份: 1982年,美国AT&amp;T公司贝尔实验室的Bjarne Stroustrup博士在c语言的基础上引入并扩充了面向对象的概念,发明了—种新的程序语言。为了表达该语言与c语言的渊源关系,它被命名为C++。而Bjarne Stroustrup(本贾尼·...

宁静致远的博客 2352
上一篇: c++ 重载 覆盖 隐藏
下一篇: pthread_cond_wait 与 pthread_mutex_lock 合作使用详解
TuxedoLinux
博客等级 码龄9年 268粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值