Всем привет! Случайно вспомнил, что мой блог про программирование, математику ващет и в целом создан для просвещения мешков с костями и мясом.
Поэтому хочу рассказать про <a href="https://en.cppreference.com/w/cpp/thread/recursive_mutex">std::recursive_mutex</a>
. Он такой же, как и std::mutex
, но запоминает номер потока, из которого был заблокирован и вторую блокировку в том же потоке игнорирует. В моём случае был приблизительно такой код:
struct Item {
~Item() {
getStorage().RemoveItem(otherItemId);
getStorage().AddItem(newItem);
}
}
using ItemPtr = std::shared_ptr<Item>;
using ItemRef = const ItemPtr&;
using ItemId = std::size_t;
class Storage {
public:
ItemId AddItem(ItemRef item)
{
std::scoped_lock lock(m_itemsGuard);
m_items.push_back(item);
return m_items.size() - 1;
}
bool HasItem(ItemId id) {
return m_items.size() < id;
}
void RemoveItem(ItemId id)
{
std::scoped_lock lock(m_itemsGuard);
auto item = m_items.at(id);
m_items.erase(m_items.begin() + id);
item.reset(); // Тут повторно заходим в RemoveItem
}
private:
std::vector<ItemPtr> m_items;
std::recursive_mutex m_itemsGuard;
};
void test() {
Storage storage;
auto itemId = storage.AddItem(std::make_shared<Item>());
storage.RemoveItem(itemId); // здесь проблема
}
Обратите внимание, что в деструкторе Item
есть обращение к методам Storage
, а там на каждое изменение массива стоит блокировка мьютекса. И оно попадет в вечный lock с std::mutex
. А на этом у меня всё. Enjoy!