C++11 std::thread 和虚函数绑定(bind)

标签 c++ multithreading thread-safety race-condition

我遇到了奇怪的 C++ 代码行为,不确定这是编译器错误还是我的代码的未定义/未指定行为。这是代码:

#include <unistd.h>
#include <iostream>
#include <thread>

struct Parent {
    std::thread t;

    static void entry(Parent* p) {
        p->init();
        p->fini();
    }

    virtual ~Parent() { t.join(); }

    void start() { t = std::thread{entry, this}; }

    virtual void init() { std::cout << "Parent::init()" << std::endl; }
    virtual void fini() { std::cout << "Parent::fini()" << std::endl; }
};

struct Child : public Parent {
    virtual void init() override { std::cout << "Child::init()" << std::endl; }
    virtual void fini() override { std::cout << "Child::fini()" << std::endl; }
};

int main() {
    Child c;

    c.start();
    sleep(1); // <========== here is it

    return 0;
}

代码的输出如下,这并不奇怪:

Child::init()
Child::fini()

但是,如果函数调用“sleep(1)”被注释掉,输出将是:

Parent::init()
Parent::~fini()

在 Ubuntu 15.04 上测试,gcc-4.9.2 和 clang-3.6.0 表现出相同的行为。编译器选项:

g++/clang++ test.cpp -std=c++11 -pthread

它看起来像一个竞争条件(在线程启动之前 vtable 没有完全构建)。这段代码格式错误吗?编译器错误?或者它应该是这样的?

最佳答案

@KerrekSB commented:

The thread uses the child object, but the child object is destroyed before the thread is joined (because the joining only happens after the destruction of the child has begun).

Child 对象在 main 结束时被销毁。 Child 析构函数被执行,并有效地调用了 Parent 析构函数,其中 Parent 基(没有这样)和数据成员(线程对象)是被毁。随着析构函数在基类链中向上调用,对象的动态类型发生变化,其顺序与构造期间的变化顺序相反,因此此时对象的类型为Parent.

线程函数中的虚拟调用可以发生在 Child 析构函数调用之前、重叠或之后,在重叠的情况下,有一个线程访问存储(实际上,vtable 指针) 正在被另一个线程更改。所以这是未定义的行为。

关于C++11 std::thread 和虚函数绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33172001/

相关文章:

java - 并发 HashMap 迭代器 :How safe is it for Threading?

java - 为什么 akka 需要不可变消息

c++ - 处理二进制文件 - 模板函数问题

c++ - 在重载的全局新运算符中使用静态对象导致核心转储运行时错误

c - 有透明的网络加速库吗?

c# - 锁定字段或局部变量?

c++ - 成员函数何时超出范围?

c++ - 从工作线程输出到 cin (c++)

c++ - 如果 + 运算符重载的第二个操作数在 C++ 中为零,如何返回第一个操作数?

java - 为什么这里需要同步?