我实现了一个自己的Runnable来调用线程:
#include <iostream>
#include <memory>
#include <thread>
class Runnable {
public:
Runnable(): running_thread_(nullptr) {}
void run() {
if(running_thread_)
return;
running_thread_ = std::unique_ptr<std::thread>(new std::thread(&Runnable::run_thread, this));
}
virtual ~Runnable() {
stop();
}
void stop() {
if (running_thread_ && running_thread_->joinable()) {
running_thread_->join();
}
running_thread_ = nullptr;
}
protected:
void run_thread()
{
std::cout << "a" << std::endl;
start();
std::cout << "b" << std::endl;
}
virtual bool start() = 0;
private:
std::unique_ptr<std::thread> running_thread_;
};
class TestRunner : public Runnable {
public:
virtual ~TestRunner() {
};
protected:
bool start() override {
return true;
}
};
int main() {
auto testRunner = std::make_shared<TestRunner>();
testRunner->run();
}如果运行代码,将得到以下输出:
/home/vagrant/tmp/clionTestProject/cmake-build-debug/clionTestProject
a
pure virtual method called
terminate called without an active exception
Process finished with exit code 134 (interrupted by signal 6: SIGABRT)看起来,start()是在TestRunner的析构函数之后调用的。对Runnable()的析构函数的stop()调用没有帮助,因为TestRunner的析构函数是在之前执行的。至少我发现可以通过在TestRunner的析构函数中调用stop来修复它:
virtual ~TestRunner() {
stop();
};使用这个析构函数,一切都如预期的那样工作。但是,我不想在派生类中调用stop()方法,所有的线程处理都将在Runnable中完成。你知道怎么解决这个问题吗?
那么,我要搜索的是:有什么方法可以防止在线程完成之前调用TestRunner的析构函数吗?
发布于 2021-07-08 10:10:41
另一种选择可以是不让TestRunner继承Runnable,而是让Runnable拥有一个TestRunner成员变量。这完全避免了这个问题。
我在这里删除了智能指针,因为我认为它们只是从派生类对象可能在线程脚下被破坏这一实际问题中获得了关注。也就是说,在调用virtual成员函数之前,以及在线程运行并处理派生类中的成员变量时。
#include <atomic>
#include <iostream>
#include <thread>
#include <type_traits>
#include <utility>
// A base class for TestRunner
class RunnerBase {
public:
void stop() { terminate_ = true; }
bool terminated() const { return terminate_; }
virtual void operator()() = 0;
private:
std::atomic<bool> terminate_ = false;
};Runnable构造函数现在将其参数传递给成员变量(在本例中为TestRunner类型)。
template<class T>
class Runnable final {
public:
static_assert(std::is_base_of_v<RunnerBase, T>, "T must inherit from RunnerBase");
template<class... Args> // constructor forwarding to the runner
Runnable(Args&&... args) : runner{std::forward<Args>(args)...} {}
void run() {
if(running_thread_.joinable()) return;
running_thread_ = std::thread(&Runnable::run_thread, this);
}
~Runnable() { stop(); }
void stop() {
if (running_thread_.joinable()) {
runner.stop();
running_thread_.join();
}
}
protected:
void run_thread() {
std::cout << "a" << std::endl;
runner();
std::cout << "b" << std::endl;
}
private:
std::thread running_thread_;
T runner;
};TestRunner继承自RunnerBase,如果是终止时间,可以检查terminated()。
#include <vector>
class TestRunner : public RunnerBase {
public:
TestRunner(size_t data_size) : foo(data_size) {}
void operator()() override {
while(!terminated()) {
// work on data that only exists in the derived class:
for(auto& v : foo) ++v;
std::cout << '.';
}
}
private:
// Some data that the thread works on that wóuld get destroyed before
// the base class destructor could call stop() if inheriting `Runnable`
std::vector<int> foo;
};创造只是略有不同。
int main() {
Runnable<TestRunner> testRunner(1024U);
testRunner.run();
}发布于 2021-07-08 06:53:45
一种使用std::shared_ptr<TestRunner>的方法是“猪背”。我不太喜欢它,但它可以工作(直到您决定去掉共享指针)。
class Runnable : public std::enable_shared_from_this<Runnable>{
...
running_thread_ = std::unique_ptr<std::thread>(new std::thread(&Runnable::run_thread, shared_from_this()));
...
static void run_thread(std::shared_ptr<Runnable> s)
{
std::cout << "a" << std::endl;
s->start();
std::cout << "b" << std::endl;
}
...这保证了在执行run_thread时不会销毁可运行的对象。
发布于 2021-07-08 05:45:30
问题是main()方法在其他线程执行run_thread()之前返回(因此删除了run_thread()对象)。因此,当run_thread()在另一个线程中执行时,它被调用到一个已经部分销毁的对象上(即,主线程已经调用了~TestRunner() ),调用未定义的行为就会发生糟糕的事情。
如果将testRunner->stop()添加到main()的末尾,则可以避免问题,因为这可以确保main()在派生线程退出之前不会返回(因此不会销毁TestRunner对象)。
能够依靠~Runnable()中的~Runnable()调用自动处理它是很好的,但是当~Runnable()执行时,已经太晚了,您的对象的子类层已经被销毁,并且您的线程已经试图使用一个破碎的对象。
https://stackoverflow.com/questions/68296163
复制相似问题