Я проектирую небольшую библиотеку как часть игрушечного проекта. Упрощенно, это было бы что-то вроде:
class FoobarManager {
public:
// Creates a new Foobar object internally an returns its ID
int createNewFoobar();
// Makes the Foobar object identified by foobarId frobnicate
void frobnicate(int foobarId);
// Removes the internal Foobar object identified by foobarId
void removeFoobar(int foobarId);
private:
// ID - Foobar
std::map<int, Foobar> allFoobars;
};
Идея состоит в том, что у меня может быть несколько Foobar
одновременно, у каждого из них будет идентификатор, который я могу использовать, чтобы попросить FoobarManager манипулировать ими. Моя цель сделать библиотеку:
- Потокобезопасен: не делать никаких предположений о том, из какого потока вызывается каждый метод.
- Синхронный: я хочу, чтобы
createNewFoobar()
возвращал Foobar
, а не предоставлял onFoobarCreated()
обратный вызов.
- Как можно более независимо с точки зрения различных
Foobar
объектов: не блокируйте все Foobar
объекты, когда происходит обморок.
Кажется, я не могу найти хорошую модель блокировки, чтобы выполнить все из них. Я думаю, мне нужно по крайней мере mutex
на Foobar
, и еще mutex
для управления вставкой / удалением на карте.
Получение вставки новых объектов работать вместе с frobnicate
кажется простым:
int createNewFoobar() {
std::lock_guard<std::mutex> mapLock(mapMutex);
allFoobars[++nextId] = Foobar();
return nextId;
}
void frobnicate(int foobarId) {
// Not using lock_guard because we need to intertwine with another lock
mapMutex.lock();
if (allFoobars.count(foobarId) == 0) return;
Foobar& fb = allFoobars.at(foobarId);
// Lock on the Foobar mutex
// ...
mapMutex.unlock();
fb.frobnicate();
// Unlock the Foobar mutex
// ...
}
Однако я не могу понять, как избавиться от определенного Foobar
на карте (и его мьютекса), не сделав недействительными ссылки на него из frobnicate()
. Есть ли способ достичь этого?
Я рассмотрел сериализацию всех вызовов в очередь сообщений и использование внутренних асинхронных обратных вызовов, используя ожидания блокировки, чтобы выглядеть синхронными извне. Это будет потокобезопасным и будет выглядеть синхронно, но не будет соответствовать пункту 3.