C++ 试图访问正在销毁的对象

C++ 试图访问正在销毁的对象,c++,multithreading,c++11,c++14,C++,Multithreading,C++11,C++14,我有一个对象,其中包含一个线程,该线程间接访问该对象,如下所示: #include <iostream> #include <thread> #include <atomic> class A; class Manager { public: Manager(void) = default; void StartA(void) { a = std::make_unique<A>(*this); }

我有一个对象,其中包含一个线程,该线程间接访问该对象,如下所示:

#include <iostream>
#include <thread>
#include <atomic>

class A;

class Manager
{
public:
    Manager(void) = default;
    void StartA(void)
    {
        a = std::make_unique<A>(*this);
    }
    void StopA(void)
    {
        a = nullptr;
    }
    A& GetA(void)
    {
        return *a;
    }
private:
    std::unique_ptr<A> a;
};

class A
{
public:
    A(Manager& manager)
        : manager{manager},
        shouldwork{true},
        thread{[&]{ this->Run(); }}
    {
    }
    ~A(void)
    {
        shouldwork = false;
        thread.join();
    }
private:
    Manager& manager;
    std::atomic<bool> shouldwork;
    std::thread thread;
    void Run(void)
    {
        while (shouldwork)
        {
            // Here goes a lot of code which calls manager.GetA().
            auto& a = manager.GetA();
        }
    }
};

int main(int argc, char* argv[])
try
{
    Manager man;
    man.StartA();
    man.StopA();
}
catch (std::exception& e)
{
    std::cerr << "Exception caught: " << e.what() << '\n';
}
catch (...)
{
    std::cerr << "Unknown exception.\n";
}
#包括
#包括
#包括
甲级;
班级经理
{
公众:
经理(无效)=默认值;
空星(空星)
{
a=标准::使_唯一(*此);
}
无效StopA(无效)
{
a=空PTR;
}
A&GetA(无效)
{
返回*a;
}
私人:
std::唯一的ptr a;
};
甲级
{
公众:
A(经理和经理)
:manager{manager},
应该工作{true},
线程{[&]{this->Run();}
{
}
~A(无效)
{
shouldwork=false;
thread.join();
}
私人:
经理&经理;
原子应能工作;
标准:螺纹;
无效运行(无效)
{
虽然(应该工作)
{
//这里有很多调用manager.GetA()的代码。
auto&a=manager.GetA();
}
}
};
int main(int argc,char*argv[])
尝试
{
经理;
男。斯塔塔();
人。停止();
}
捕获(标准::异常&e)
{
std::cerr在
StopA()
中设置
a=nullptr;
,这反过来会破坏
a
对象,对其成员的所有进一步访问都会导致未定义的行为(可能导致分段错误)

只需将
a=nullptr;
移动到
Manager
的析构函数,就可以解决这个问题。更好的是,当
Manager
的析构函数运行时,允许
std::unique\u ptr
的RAII机制销毁
a
对象(即完全删除代码行)

使用时,仔细控制成员变量非常重要,尤其是“停止变量/控制”(此处的
shouldwork=false;
)。允许管理者直接访问变量或通过方法在活动对象销毁之前停止活动对象


这里的一些代码看起来不合适或模糊,例如
a=std::make_unique(*this);
。重新设计有助于简化一些代码。可以删除
管理器

class A
{
public:
    A(): shouldwork{true}, thread{[&]{ this->Run(); }}
    {
    }
    void StopA()
    {
        shouldwork = false;
        thread.join(); 
    }
private:
    std::atomic<bool> shouldwork;
    std::thread thread;
    void Run(void)
    {
        while (shouldwork)
        {
            // code...
        }
    }
};

而你的
main
保持原样。

你会得到一个分段错误,因为你试图去引用
nullptr
。调用StopA将
a
设置为
nullptr
。GetA去引用它
返回*a
好的,我正在尝试应用你的修复,不幸的是真正的代码有点复杂,现在我得到了e异常在
thread.join()
中,如果需要更多帮助,我将更新该问题。@FaTony.Ok,这可能是因为它不再是
joinable()
,这意味着其他线程已经加入它,或者它已被分离。请注意正在移动的线程,以及您加入的对象不再代表该线程。好的,现在我仍然在
GetA
上获得segfault,而另一个线程在
A
构造函数中。好的,我通过将线程开始代码移动到一个分隔符中来修复它e
Start
功能。
class A
{
public:
    A() : shouldwork{true}, thread{[&]{ this->Run(); }} {}
    void StopA() { shouldwork = false; thread.join(); }
private:
    void Run();
    std::atomic<bool> shouldwork;
    std::thread thread;
};

class Manager
{
public:
    Manager() = default;
    void StartA(void)
    {
        a = std::make_unique<A>();
    }
    void StopA(void)
    {
        a->StopA();
    }
    A& GetA(void)
    {
        return *a;
    }
private:
    std::unique_ptr<A> a;
};

void A::Run()
{
    while (shouldwork)
    {
        // Here goes a lot of code which calls manager.GetA().
        auto& a = manager.GetA();
    }
}