c++ - 使用共享指针来自另一个线程的纯虚拟调用
问题描述
我觉得很奇怪。请帮我解释一下。我有一个在单独的线程中启动无限循环的类,以及两个继承它的类。其中一个类实现了要在 as 外部触发的接口std::shared_ptr
,而另一个类则持有该接口 as std::weak_ptr
。请看下面的代码。很抱歉有很多代码,我试图尽可能短地重现错误。为什么有时我有纯虚Sender::notify
函数调用?据我所知std::shared_ptr
是可重入的。
#include <iostream>
#include <memory>
#include <thread>
#include <atomic>
#include <list>
#include <mutex>
class Thread : private std::thread {
std::atomic_bool run {true};
public:
Thread() : std::thread([this](){ thread_fun(); }) {}
void thread_fun() {
while (run) loop_iteration();
}
virtual void loop_iteration() = 0;
virtual ~Thread() {
run.exchange(false);
join();
std::cout << "Thread released." << std::endl;
}
};
class Sender : public Thread {
public:
class Signal{
public:
virtual void send() = 0;
virtual ~Signal(){}
};
void add_receiver(std::weak_ptr<Signal> receiver) {
std::lock_guard<std::mutex> lock(receivers_mutex);
receivers.push_back(receiver);
}
void notify() {
std::lock_guard<std::mutex> lock(receivers_mutex);
for (auto r : receivers)
if (auto shp = r.lock())
shp->send(); //Somethimes I get the pure virtual call here
}
private:
std::mutex receivers_mutex;
std::list<std::weak_ptr<Signal>> receivers;
void loop_iteration() override {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
notify();
}
};
class Receiver : public Thread, public Sender::Signal {
std::atomic_bool notified {false};
public:
void send() override {
notified.exchange(true);
}
private:
void loop_iteration() override {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
std::cout << "This thread was " << (notified? " " : "not ") << "notified" << std::endl;
}
};
int main() {
std::shared_ptr<Thread>
receiver = std::make_shared<Receiver>(),
notifier = std::make_shared<Sender>();
std::dynamic_pointer_cast<Sender>(notifier)->add_receiver(
std::dynamic_pointer_cast<Sender::Signal>(receiver));
receiver.reset();
notifier.reset();
return 0;
}
解决方案
在构造和销毁过程中,多态性不会像您期望的那样工作。当前类型是仍然存在的最派生类型。当你在你的对象Thread::~Thread
的Sender
一部分已经被完全破坏时,调用它的覆盖是不安全的。
当thread_fun
尝试loop_iterator()
在构造函数完成之前或析构函数启动之后运行时,它不会进行多态分派,而是调用Thread::loop_iteration
纯虚函数(= 0
)。
请参阅https://en.cppreference.com/w/cpp/language/virtual#During_construction_and_destruction
这是一个演示:https ://godbolt.org/z/4vsPGYq97
对象在derived
一秒钟后被销毁,此时您会看到输出更改,表明被调用的虚函数在该点发生更改。
我不确定这段代码是否有效,或者derived
在执行其成员函数之一时破坏对象的一部分是否是未定义的行为。
推荐阅读
- rest - 如何在 OneDrive 上获取已与我共享的单个文件?
- postgresql-9.5 - 在 Postgresql-9.5 上安装 PL/Java 时如何修复“SPI_ERROR_UNCONNECTED”错误
- javascript - 关于日期的简单 JS 问题(全年 + 1)
- php - 从 CSV 文件导入数据时如何消除“注意:未定义偏移量:1”错误
- android - 如何在开始目标片段中隐藏导航抽屉图标并将其显示在导航图的开始目标片段旁边
- java - 如何使用多线程调用多个服务?
- angular - 在角度 6 中隐藏特定的标题组件
- excel - 将二维表转换为列表
- python - Dask dataframe.read_csv 无法与 hdfs csv 文件一起正常工作
- excel - 如何使用 VBA 合并多个单元格