C++ 为什么父母';s方法在child';毁灭

C++ 为什么父母';s方法在child';毁灭,c++,multithreading,inheritance,race-condition,C++,Multithreading,Inheritance,Race Condition,我不明白为什么要运行父类的“execute”函数。我觉得有两个实例:一个用于父类,一个用于子类,但是为什么呢?实际上,这个程序正在打印“1个家长”,正如我预期的“1个孩子”或“0个家长”。如果我取消对延迟线的注释,输出将是“1个子” 我知道这个节目中有一个比赛条件。这个程序只是为了理解多线程环境中继承的工作原理 谢谢大家! #include <stdio.h> #include <stdlib.h> #include <iostream> #include &

我不明白为什么要运行父类的“execute”函数。我觉得有两个实例:一个用于父类,一个用于子类,但是为什么呢?实际上,这个程序正在打印“1个家长”,正如我预期的“1个孩子”或“0个家长”。如果我取消对延迟线的注释,输出将是“1个子”

我知道这个节目中有一个比赛条件。这个程序只是为了理解多线程环境中继承的工作原理

谢谢大家!

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <thread>

class Parent
{
public:
    std::thread myThread;
    int a;
    Parent() {
        this->myThread = std::thread();
        this->a = 0;
    }
    void start()
    {
        this->myThread = std::thread(&Parent::execute, this);
    }
    virtual void execute() {
        std::cout << a << " Parent" << std::endl;
    }
    virtual ~Parent() {
        while(!this->myThread.joinable());
        this->myThread.join();
    }

};

class Child : public Parent
{
public:
    Child() {
        this->a = 1;
    }
    void execute() override {
        std::cout << a << " Child" << std::endl;
    }
    ~Child() {

    }

};

int main()
{
    std::cout << "Init" << std::endl;
    Child * chld = new Child();
    chld->start();
    //std::this_thread::sleep_for(std::chrono::milliseconds(x));
    std::cout << "Delete" << std::endl;
    delete chld;
    return 0;
}
#包括
#包括
#包括
#包括
#包括
班级家长
{
公众:
线程读取;
INTA;
父项(){
这->myThread=std::thread();
这->a=0;
}
void start()
{
this->myThread=std::thread(&Parent::execute,this);
}
虚拟空执行(){
标准::cout a=1;
}
void execute()重写{

std::cout您的程序有未定义的行为,这意味着“一切都可能发生”

您启动一个新线程,该线程持有指向对象的指针(this)。该线程稍后将调用一个虚拟方法,这意味着它需要使用它指向的对象中的数据。vtable指针本身是类的某种数据。因为您从另一个线程中删除了对象,指针(this)只是指向已破坏的对象并从已删除的对象访问数据(vtable)是未定义的行为

您的观察结果取决于编译器实现,也可能取决于优化级别。在解构过程中,您的编译器可能会将vtable指针倒回基类指针。而且,对象的内存不会被任何其他内容覆盖(甚至是未定义的内容!)您可以在销毁后观察对基函数的调用。但这不是您可以依赖的,因为如果您使用对象的数据成员(此处为vtable指针),则在销毁后根本不允许使用任何对象


简而言之:您的代码包含一个bug,任何事情都可能发生,因为它是未定义的行为。

这与线程无关。您可以同步复制整个过程,包括未定义的行为

类的单线程版本:

#include <iostream>
#include <string>

class Parent
{
public:
    int a;
    Parent() : a(0) {}
    virtual ~Parent() {}

    virtual void execute() {
        std::cout << a << " Parent" << std::endl;
    }
};

class Child : public Parent
{
public:
    Child() {
        a = 1;
    }
    void execute() override {
        std::cout << a << " Child" << std::endl;
    }
};

您的代码显示未定义的行为(在您的情况下导致
Parent::execute
调用)由于线程创建和
子对象
对象销毁之间存在争用条件。若要解决此问题,您可以在
父类
中定义正确的启动和停止方法,并在
子对象
析构函数中调用
停止
,以防止在线程联接之前将其销毁

class Parent
{
public:
    Parent(): myThread_() {
        std::cout << "Parent CTor" << std::endl;
    }
    virtual ~Parent() = default;
    bool start()
    {
        std::cout << "start" << std::endl;
        if (myThread_.joinable()) {
            std::cout << "already started" << std::endl;
            return false;
        }
        myThread_ = std::thread([this]() {
            execute();
        });
        return true;
    }
    bool stop() {
        std::cout << "stop" << std::endl;
        if (!myThread_.joinable()) {
            std::cout << "not started" << std::endl;
            return false;
        }
        myThread_.join();
        return true;
    }
    virtual void execute() = 0;

private:
    std::thread myThread_;
};

class Child : public Parent
{
public:
    Child() {
        std::cout << "Child CTor" << std::endl;
    }
    ~Child() override {
        stop();
    }
    void execute() override {
        std::cout << "Child::execute()" << std::endl;
    }
};

int main()
{
    std::cout << "Init" << std::endl;
    Child * chld = new Child();
    chld->start();
    std::cout << "Delete" << std::endl;
    delete chld;
    return 0;
}

Parent::execute()
仍然调用父函数。不总是这样,如果在开始和删除之间设置延迟,则将调用
Child::execute()
functionThreading在这里不起任何作用。如果您的write
Parent::execute
显式地告诉您要调用哪个函数,而不是让运行时解析虚拟函数调用。我昨天看到了完全相同的问题,但它似乎被删除了。我还解释了这是一个reace条件,因为子级被删除。@FrançoisAndrieux尝试使用我注释的延迟执行函数,它将执行子函数,因为函数重写。这实际上不一样,因为在我的代码中对象没有被销毁,因为主线程正在等待第二个线程加入。我错了吗?如果子析构函数在线程I之前已经执行了如果已计划,则在阻止父析构函数的同时,对其动态类型已回滚到父类的对象调用该方法。这就是您感兴趣的情况吗?因为在这种情况下,您有一个父对象,其
a
仍然具有子构造函数给定的值
1
。线程仍然是ir相关,因为您可以通过从基类析构函数调用重写的虚拟方法来查看此行为。
=== automatic lifetime ===
virtual dispatch: 1 Child
explicit static dispatch: 1 Parent
=== dynamic lifetime ===
virtual dispatch: 1 Child
explicit static dispatch: 1 Parent
=== undefined behaviour ===
explicit static dispatch: 1 Parent
Segmentation fault      (core dumped) ./a.out
class Parent
{
public:
    Parent(): myThread_() {
        std::cout << "Parent CTor" << std::endl;
    }
    virtual ~Parent() = default;
    bool start()
    {
        std::cout << "start" << std::endl;
        if (myThread_.joinable()) {
            std::cout << "already started" << std::endl;
            return false;
        }
        myThread_ = std::thread([this]() {
            execute();
        });
        return true;
    }
    bool stop() {
        std::cout << "stop" << std::endl;
        if (!myThread_.joinable()) {
            std::cout << "not started" << std::endl;
            return false;
        }
        myThread_.join();
        return true;
    }
    virtual void execute() = 0;

private:
    std::thread myThread_;
};

class Child : public Parent
{
public:
    Child() {
        std::cout << "Child CTor" << std::endl;
    }
    ~Child() override {
        stop();
    }
    void execute() override {
        std::cout << "Child::execute()" << std::endl;
    }
};

int main()
{
    std::cout << "Init" << std::endl;
    Child * chld = new Child();
    chld->start();
    std::cout << "Delete" << std::endl;
    delete chld;
    return 0;
}
terminate, pure virtual method called