在 C++ 中运行类成员函数的线程

Posted

技术标签:

【中文标题】在 C++ 中运行类成员函数的线程【英文标题】:run threads of class member function in c++ 【发布时间】:2011-01-12 08:27:07 【问题描述】:

正如标题所说。 以下是我的代码骨架。

class CLASS

public:
    void A();
private:
    DWORD WINAPI B(LPVOID);
;

void CLASS::A()

    DWORD (WINAPI CLASS::*thread)(LPVOID) = &CLASS::B;
    ...
    CreateThread(NULL, 0, thread, &arg, 0, NULL);
    ...

函数B需要CLASS的成员变量。 但是当我编译这个时,我得到了一个错误代码。 它是“无法将参数 3 从 'DWORD (__stdcall CLASS::* )(LPVOID)' 转换为 'LPTHREAD_START_ROUTINE'”或类似的东西。 不知道英文环境是不是也一样。

有人可以帮忙吗?

【问题讨论】:

【参考方案1】:

说真的,使用std::thread

class CLASS

public:
    void A();
private:
    void B(your args go here);
;

void CLASS::A()

    std::thread t(&CLASS::B, this, your args go here);
    // when done
    t.join();
    // or
    t.detach();

说明

您的代码无法编译,因为CreateThread 是一个 C API,需要一个非成员 C 函数作为回调。为了调用 C++ 类的非静态成员函数,调用者需要了解this 指针以及如何正确使用它。由于 WinAPI 显然没有 this 并且不期望成员函数指针,因此这不可能工作。您的编译器会捕捉到这一点并将其报告为类型不匹配错误。

这就是CreateThreadlpParameter 发挥作用的地方。它允许您通过该参数传递this。然而它并没有改变CreateThread 不知道如何调用C++ 成员函数的事实。因此,您有责任将其包装在一个静态函数中,该函数将执行成员函数调用:

class CLASS

public:
    void A();
private:
    DWORD B();
    static DWORD s_B(LPVOID);
;

DWORD CLASS::s_B(LPVOID that) 
    return ((CLASS*)that)->B();


void CLASS::A() 
    CreateThread(NULL, 0, s_B, this, 0, NULL);

这就是@Nawaz 在他们的回答中所做的,除了稍微更笼统的方式。

尽管这种方法有效,但它显然也有缺点:

很冗长。 它不便携。 您需要通过不同的方式(例如通过您的班级成员)传递您原来的 args

std::thread 已经为您完成了所有这些工作。

【讨论】:

+1。是的,绝对要使用 std::/boost::thread。此外,您甚至不需要绑定:boost::thread t(&CLASS::B, this, your args if any go here);。就这么简单。【参考方案2】:

如果它是成员函数,您必须将回调函数定义为 static 函数!


更好的设计:定义一个可重用的类!

来自我的previous answer:(稍作修改)

更好的办法是定义一个 可重用 类,该类具有纯虚函数 run() 以由派生的 thread 类实现。以下是它的设计方式:

//runnable is reusable class. All thread classes must derive from it! 
class runnable

public:
    virtual ~runnable() 
    static DWORD WINAPI run_thread(LPVOID args)
    
        runnable *prunnable = static_cast<runnable*>(args);
        return prunnable->run();
    
 protected:
    virtual DWORD run() = 0; //derived class must implement this!
;

class Thread : public runnable //derived from runnable!

public:
    void newthread()
    
        CreateThread(NULL, 0, &runnable::run_thread, this, 0, NULL);
    
protected:
    DWORD run() //implementing the virtual function!
    
         /*.....your thread execution code.....*/
    

【讨论】:

我会使用模板,而不是运行时继承。但总体思路是正确的。 @DeadMG : 是的..那会有点快...只是我写这篇文章的时候没想到... @Nawaz:您应该始终首先考虑 CRTP,然后再考虑运行时继承。 @DeadMG:是的。我现在意识到了。 :-) 必须派生一个类并重写一个函数来运行线程,这有点过时和尴尬的设计。最好使用 boost::thread 或接受函子作为线程函数并且不需要派生任何东西的类似机制。【参考方案3】:

你必须使那个成员函数static

这里的问题是每个非静态成员函数都有一个隐含的this 参数,这实际上是编译器试图告诉你的——你的非静态成员函数的签名与你预期的不同。

另见this answer to a closely related question。

【讨论】:

在 OP 开始获得任何聪明的想法之前 - 非静态类成员函数指针是奇怪的。不仅调用约定与简单的func(classname *this, ...) 不同,指针表示 也很奇怪——在某些情况下,类成员函数指针最终可能是普通函数指针大小的 2 倍,所以不要甚至没有考虑强迫演员:)

以上是关于在 C++ 中运行类成员函数的线程的主要内容,如果未能解决你的问题,请参考以下文章

使用std::future监控线程执行类成员函数(c++)? [复制]

运行时在构造函数中初始化向量类成员——C++

如何在一个类中执行 C++ 多线程(将线程引用保持为成员 var)

linux 之 pthread_create 实现类的成员函数做参数

使用类成员的 C++ 函数回调并在 main 中运行

有没有办法使用类的成员函数在并发线程中运行而无需在 C++11 中传递 *this?