【问题标题】:c++11 unexpected polymorphism beahviour when using std::threadc++11 使用 std::thread 时出现意外的多态行为
【发布时间】:2014-06-19 11:53:53
【问题描述】:

基于C++ polymorphism with variadic function parameter我尝试编写类似的(非模板化,带构造函数)程序

代码:

#include <thread>
#include <iostream>
#include <vector>

class Base
{
public:
    Base (int count) { run(count); } // this-> does not help

    virtual void run (int count) { // non-virtual does not help eighter
        for (int i=0; i<count; ++i)
            threads.emplace_back(std::ref(*this));
    }

    virtual ~Base () {
        for (auto& t : threads)
            t.join();
    }

    virtual void operator() () = 0;

protected:
    std::vector< std::thread > threads;
};


class Derived : public Base
{
public:
    using Base::Base;
    virtual void operator() () { std::cout << "d"; }
};


int main()
{
    Derived d(4);
    std::cout << std::endl;
    return 0;
}

预期结果:

dddd

真实结果(Ubuntu 14.04,gcc v4.8.2):

pure virtual method called
pure virtual method called
terminate called without an active exception
terminate called without an active exception
dAborted (core dumped)

请注意,Derived::operator() 至少被真正调用过一次(d 在最后一行,几乎总是如此)。

即使代码非常简单并且与原始代码几乎相同(参见上面的链接),它也不起作用。我花了好几个小时来解决这个问题。

目标是用多个线程构造Derived。这个数量的线程将被执行(在构造函数中)并在析构函数中加入。 operator() 应该用作线程体函数(如在原始代码中)。此外,为了提供多态性,它应该是虚拟的。

就我而言,run 传递 *this(出于某种原因)键入为 Base,而不是 Derived,因此线程执行 Base::operator(),这是纯虚拟的

附加问题:有没有办法标记operator()受保护?

谁能帮帮我?谢谢。

编辑:

根据Billy ONeal的回答我重写了代码,所以Derived构造函数调用了run,但是没有任何成功

#include <thread>
#include <iostream>
#include <vector>

class Base
{
public:
    virtual void run (int count) { // non-virtual does not help eighter
        for (int i=0; i<count; ++i)
            threads.emplace_back(std::ref(*this));
    }

    virtual ~Base () {
        for (auto& t : threads)
            t.join();
    }

    virtual void operator() () = 0;

protected:
    std::vector< std::thread > threads;
};


class Derived : public Base
{
public:
    Derived (int count) { run(count); }
    virtual void operator() () { std::cout << "d"; }
};


int main()
{
    Derived d(4);
    std::cout << std::endl;
    return 0;
}

结果会随时间而变化 - 这就是我得到的所有结果

1) d
2) dd
3) ddd
4) dddd
5) d
    pure virtual method called
    terminate called without an active exception
    ddAborted (core dumped)

尤其是5)我无法解释。

我在 Derived d(4); 周围添加了 {...} 作为匿名块,以强制在行结束程序终止的 endl 之前执行析构函数,但因为我只有

pure virtual method called
terminate called without an active exception
ddAborted (core dumped)

【问题讨论】:

  • 我不认为代码是一样的。
  • 好吧,我想这很相似..

标签: c++ multithreading c++11 polymorphism pure-virtual


【解决方案1】:

您的代码存在竞争条件(因此行为未定义)。当您在Base 的构造函数中启动线程时,该线程将立即尝试在该对象上调用operator()。但是Derived的构造函数还没有运行,所以operator()仍然是来自Base的那个,它是纯虚拟的。另一个有效的执行是 Base 的构造函数和 Derived 的构造函数在线程实际开始之前完成,这将给出您期望的行为,但结果不太可能。

您关于 this-&gt;run 没有帮助的评论是有道理的,因为输出抱怨调用了纯虚拟成员函数,而 run 不是纯虚拟成员,只有 operator() 是。

【讨论】:

  • 好的,我明白了这个问题,你能提出一些解决方案吗?我相信它一定存在,因为我在其他语言中没有遇到类似的问题。请看一下我在答案中的编辑 - 这真的很奇怪。谢谢你 :)
  • 您编辑的代码基本上有同样的问题,但是现在当您加入线程时,Derived 析构函数已经运行。您需要阻止 Derived 析构函数完成,直到所有线程都调用了虚函数。最初您启动线程运行之前 Derived 对象的生命周期开始,现在您允许它们继续运行之后 Derived 对象的生命周期结束。线程只能在Derived 对象的生命周期内调用派生函数!
  • @petrbel 一般来说,在基类的构造函数中生成线程是一个糟糕的主意——尤其是因为完整的对象还没有完成构造。最好让客户端告诉对象启动它的线程,或者将整个对象创建包装在一个工厂函数中,该函数在构造后自动启动线程。
  • @Casey - 好吧,我想除了在不产生线程的情况下构造对象之外别无他法,并且在第一次调用可能使用它们的任何函数时这样做 - 这很丑陋,但它应该可以工作,不不是吗?
猜你喜欢
  • 2017-09-07
  • 1970-01-01
  • 1970-01-01
  • 2016-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多