【问题标题】:How to use C++11 thread with instance method? [duplicate]如何将 C++11 线程与实例方法一起使用? [复制]
【发布时间】:2014-01-02 08:47:57
【问题描述】:

我有一个 Player 类,一些子类 Player1、Player2、Player3 使用 C++ 扩展了 Player。
类 Player 有一个方法“run”,所有 Player1,2,3 都会覆盖“run”来做不同的事情。

class Player {
public:
    virtual void run();
}
class Player1: public Player {
public:
    void run();
}

在“main”函数中,我将创建 Player1、2、3 的一些实例
以及这些实例的一些 C++11 线程调用方法“运行”。

int main() {
    Player1 player1;
    Player2 player2;
    Player3 player3;
    Thread thread1(player1.run, this);
    Thread thread2(player2.run, this);
    Thread thread3(player3.run, this);
    thread1.join();
    thread2.join();
    thread3.join();

    return 0;
}

我试过了,我知道它不起作用,
所以我尝试使用另一个函数来调用实例方法。

function doRun1(Player1 player){
    player.run();
}

int main() {
    Player1 player1;
    Player2 player2;
    Player3 player3;
    Thread thread1(doRun1, player1);
    Thread thread2(doRun2, player2);
    Thread thread3(doRun3, player3);
    thread1.join();
    thread2.join();
    thread3.join();

    return 0;
}

这种方式似乎可以解决问题,但我必须创建doRun1,doRun2,doRun3....很多功能,
因为doRun1,2,3的参数需要声明是Player1,2还是3

我想不出更好的解决方案,有人可以帮我@@吗?

【问题讨论】:

  • C11 和这个有什么关系? Thread 到底是什么,构造函数的签名是什么?
  • 哇。 C11也有类吗?
  • 对不起,我的错...我知道这是什么问题@@。我的意思是 C++11 不是 C11,我会更正它...原谅我
  • void main 是非法的。请使用int main

标签: c++ c++11


【解决方案1】:

你真正要找的是std::bind

#include <thread>
#include <functional>

using namespace std;

void main()
{
    Player1 myPlayer1;

    thread thread1(bind(&Player1::run, &myPlayer1));
}

您正在做的是创建一个std::function 对象,thread 构造函数接受该对象。然后,您将作为 bind 函数的一部分“烘焙”该实例。但是要非常小心,因为当 myPlayer1 超出范围时,该指针也会这样做。你可能想要这个:

#include <thread>
#include <functional>
#include <memory>

using namespace std;

void main()
{
    shared_ptr<Player1> ptr_myPlayer1(new Player1());

    thread thread1(bind(&Player1::run, ptr_myPlayer1));
}

这允许对象的生命周期是线程的生命周期,而不是main 的生命周期。这不是你的小例子中的问题,而是现实生活中的问题。

有关绑定的更多信息,请参阅here,并阅读functional 上的整个部分。

编辑:

结合上面所说的动态调度,这也是可能的:

#include <thread>
#include <memory>

using namespace std;

void main()
{
    shared_ptr<Player> ptr_myPlayer(new Player1());  // Works because holds a pointer to base class

    thread thread1(&Player::run, ptr_myPlayer1);
}

这样您使用的是基类的虚函数,但是您使用的是指向子类实例的智能指针,因此您可以退出原始函数(假设不​​是 main 并且程序没有结束) .世界上最好的。 bind 在这里不是必需的(因为 std::thread 已经在内部这样做了,至少这是一种可能的实现方式)但它仍然是 OP 想要的“指向任何东西的函数指针”的一般概念。

【讨论】:

    【解决方案2】:

    你正在寻找这样的东西......

    class Player {
    public:
        virtual void run() = 0;
    };
    
    class Player1: public Player {
    public:
        void run(); // you must implement then for Player1, 2, 3
    };
    
    void doRun(Player * player)
    {
        player->run();
    }
    
    int main(int argc, char * argv[]) {
        Player1 player1;
        Player2 player2;
        Player3 player3;
    
        thread thread1(doRun, &player1);
        thread thread2(doRun, &player2);
        thread thread3(doRun, &player3);
    
        thread1.join();
        thread2.join();
        thread3.join();
    
        return 0;
    }
    

    如果您愿意,也可以使用 lambda 表达式:

    int main(int argc, char * argv[]) {
        Player1 player1;
        Player2 player2;
        Player3 player3;
    
        thread thread1([&] (Player * player) { player->run(); }, &player1);
        thread thread2([&] (Player * player) { player->run(); }, &player2);
        thread thread3([&] (Player * player) { player->run(); }, &player3);
    
        thread1.join();
        thread2.join();
        thread3.join();
    
        return 0;
    }
    

    或者,按照 DyP 的建议:

    int main(int argc, char * argv[]) {
        Player1 player1;
        Player2 player2;
        Player3 player3;
    
        thread thread1(&Player::run, player1);
        thread thread2(&Player::run, player2);
        thread thread3(&Player::run, player3);
    
        thread1.join();
        thread2.join();
        thread3.join();
    
        return 0;
    }
    

    【讨论】:

    • 值得一提的是,线程将在player1player2player3副本上运行。如果需要引用语义,std::reference_wrappers 是一个不错的选择(通过 std::ref(player1) 等)。此外,main 应该返回 int
    • 感谢您的解决方案!我会试试的
    • 为什么不只是thread thread1(&amp;Player::run, player1);? (指向虚拟成员函数的指针仍然允许动态调度。)
    • 你是对的 Dyp...比我的快速解决方案要好得多...我将编辑以改进答案。我只是关注了他创建的doRun(),所以我保留了它......但实际上最初的愿望只是调用Player::run() ;-)
    • 请注意最后一个示例,因为它正在获取参数的副本,这意味着每个新线程都在获取每个玩家子类对象的副本。这可能是也可能不是我们想要的。
    猜你喜欢
    • 2012-11-15
    • 2014-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-11
    相关资源
    最近更新 更多