【问题标题】:run threads of class member function in c++在 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'”或类似的东西。
不知道英文环境是不是也一样。

有人可以帮忙吗?

【问题讨论】:

    标签: c++ multithreading class


    【解决方案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 倍,所以不要甚至没有考虑强迫演员:)
    猜你喜欢
    • 2017-05-07
    • 1970-01-01
    • 1970-01-01
    • 2013-03-22
    • 1970-01-01
    • 2018-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多