I have two classes in different source file, class A in a.hpp and a.cpp, class B in b.hpp and b.cpp, A is a Singleton class, it create B object, and store it in a map, when B object iscreated, it creates a thread, and I want Bs object to remove itself from that map, which is A's member when the thread exits, but my program throw a exception.
// main.cpp
#include <iostream>
#include "a.hpp"
int main() {
A::get_instance().add(1);
int pause = 0; std::cin >> pause;
return 0;
}
// a.hpp
#ifndef __A_HPP__
#define __A_HPP__
#include <unordered_map>
#include <memory>
#include "b.hpp"
class A {
public:
void add(int i);
void remove(int i);
static A& get_instance();
private:
std::unordered_map<int, std::shared_ptr<B>> map_b_;
};
#endif
// a.cpp
#include "a.hpp"
void A::add(int i) {
std::cout << "add: " << i << std::endl;
map_b_[i] = std::move(std::make_shared<B>(i));
std::this_thread::sleep_for(3s);
}
void A::remove(int i) {
std::cout << "remove: " << i << std::endl;
if (map_b_.count(i)) {
map_b_.erase(i);
}
}
A& A::get_instance() {
static A instance;
return instance;
}
// b.hpp
#ifndef __B_HPP__
#define __B_HPP__
#include <thread>
#include <chrono>
#include <atomic>
#include <iostream>
using namespace std::chrono_literals;
class B {
public:
B(int id);
~B();
void stop();
void run();
private:
std::atomic<bool> run_flag_;
std::thread thr_;
int id_;
};
#endif
b.cpp
#include "b.hpp"
#include "a.hpp"
B::B(int id) {
id_ = id;
run_flag_ = true;
std::thread t(&B::run, this);
thr_ = std::move(t);
}
B::~B() {
std::cout << "~B(): " << id_ << std::endl;
stop();
}
void B::stop() {
run_flag_ = false;
if (thr_.joinable()) {
thr_.join();
}
}
void B::run() {
int count = 0;
while (count < 3 && run_flag_) {
std::cout << "processing id: " << id_ << std::endl;
std::this_thread::sleep_for(1s);
count ++;
}
A::get_instance().remove(id_);
}
# CMakeLists.txt
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (demo)
SET(CMAKE_BUILD_TYPE "Debug")
# 指定生成目标
add_executable(
${PROJECT_NAME}
main.cpp
a.cpp
b.cpp
)
find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} Threads::Threads)
i have run the code in my linux docker, but it throw an error, like this, i want know how can i do it to remove one itself from container.
root@53d8817a43a8:~/test-cpp/build# ./demo
add: 1
processing id: 1
processing id: 1
processing id: 1
remove: 1
~B(): 1
terminate called after throwing an instance of 'std::system_error'
what(): Resource deadlock avoided
Aborted
For what you requested I would choose that design:
Implement the Publish Subscribe design pattern (source code). The
Singletonclass inherits from theSubscriberclass. TheBclass inherits from thePublisherclass.The
Bclass hasinline static std::atomic_uint64_t unique_{ 0 }anduint64_t id_{ 0 }private data members. The constructor ofBincrements++unique_and assignsid_ = unique_. I.e. eachBobject has a unique id. Therefore eachBobject in themap_(std::unordered_map) has a unique id. IT IS IMPORTANT TO PREVENT COPYING OF B OBJECT!Bobject:b, get its id, add its pointer to themap_,subscribe(b), and finally: Callbto run its thread.The
Bclass has a member functionvoid Done(). The thread that detached by theBobject gets the address ofBobject'sDone()member function ascallbackparameter. When the thread is about to return, it calls thecallback. IT IS IMPORTANT NOT TO REMOVE/DESTROY AN OBJECT FROM THE MAP AS LONG AS ITS THREAD IS RUNNING!The implementation of
B::Done()calls:notify(id_)function (of thePublisherclass).Since the
Singletonclass is aSubscriberits instance intercepts this call on:void update(Publisher* who, void* what = 0)in the context of aBobject's thread.The
Singleton::updatefunction (override of theSubscriber) casts thevoid* whattouint64_t(gets the id) and removes theBobject from itsmap_.map_theBobject:b, by its id,unsubscribe(b), remove it from themap_.The operation of Add
Bobject and RemoveBobject are in the contexts of 2 different threads therefore these places should be guarded withstd::lock_gaurd lk(mutex_). I.e. theSingletonclass should have amutable std::mutex mutex_(mutable- M&M rule).