试图访问被销毁对象
问题描述:
我有包含间接访问这样的对象,从而一个线程的对象:试图访问被销毁对象
#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";
}
的问题是,当一个线程调用Manager::StopA
并进入的A
析构函数, A
段落内的线程Manager::GetA
。我怎样才能解决这个问题?
答
StopA()
您设置了a = nullptr;
,这反过来破坏了a
对象,并且对其成员的所有进一步访问都会导致未定义的行为(可能导致分段错误)。
只需将a = nullptr;
移至Manager
的析构函数即可解决此问题。更好的是,当Manager
的析构函数运行时(即完全移除该行代码),允许std::unique_ptr
的RAII机制销毁对象a
。
随着active objectimplementations,小心控制成员变量很重要,特别是“停止变量/控制”(这里是shouldwork = false;
)。允许管理员直接访问该变量或通过一种方法访问该变量,以在其销毁之前停止活动对象。
这里的一些代码看起来不合适或不明显, a = std::make_unique<A>(*this);
。重新设计可以帮助简化一些代码。 Manager
类可以被删除。
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...
}
}
};
的代码是沿std::thread
线为蓝本,是由一种尝试之前加入其胎面的停止更加可控。在这种情况下,析构函数留空,以模仿终结(调用std::terminate
)的结果,就像标准线程库一样。线程必须在销毁前明确加入(或分离)。
重新引入Manager
,代码可能如下所示;
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();
}
}
而您的main
保持原样。
因为尝试解引用'nullptr',所以会出现分段错误。调用StopA会将'a'设置为'nullptr'。 GetA取消引用'return * a' – Emil