c++ - 同步、线程安全接口(interface)的正确锁定模型

标签 c++ multithreading locking

我正在设计一个小型图书馆作为玩具项目的一部分。简化后,它会是这样的:

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,它们中的每一个都有一个 ID,我可以用它来请求 FoobarManager 来操作它们。我的目标是制作图书馆:

  1. 线程安全:不对调用每个方法的线程做任何假设。
  2. 同步:我希望 createNewFoobar() 返回一个 Foobar,而不是提供一个 onFoobarCreated() 回调。
  3. 从不同 Foobar 对象的角度来看,尽可能独立:当一个对象处于 frobnicating 状态时,不要锁定所有 Foobar 对象。

我似乎找不到一个好的锁定模型来满足所有这些要求。我想每个 Foobar 至少需要一个 mutex,以及另一个 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
    // ...
}

但是,我无法弄清楚如何在不使 frobnicate() 对它的引用无效的情况下摆脱 map 中的某个 Foobar(及其互斥量) .有办法实现吗?

我考虑过将所有调用序列化到一个消息队列中,并在内部使用异步回调,使用阻塞等待从外部看起来是同步的。这将是线程安全的并且看起来是同步的,但不会满足第 3 点。

最佳答案

通过在映射中存储指向 Foobar 的共享指针,您可以在 frobnicate 处理它时安全地删除它。

map :

std::map<int, std::shared_ptr<Foobar> > allFoobars;

代码

int createNewFoobar() {
    // First create the Foobar, so a lengthy creation does not hold the mutex
    std::shared_ptr<Foobar> newFoobar(std::make_shared<Foobar>());

    std::lock_guard<std::mutex> mapLock(mapMutex);
    allFoobars[nextId] = newFoobar;
    return nextId++;
}

void frobnicate(int foobarId) {
    std::map<Foobar>::iterator findFoobar;

    {
        std::lock_guard<std::mutex> mapLock(mapMutex);
        findFoobar = allFobars.find(foobarId);
        if (findFoobar == allFoobar.end())
        {
            return;
        }
    }

    findFoobar.second->frobnicate();
}

然后,即使您从 map 中删除了 Foobar,findFoobar.second 中的共享指针仍会使其保持事件状态,直到 frobnicate 终止。

关于c++ - 同步、线程安全接口(interface)的正确锁定模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53222873/

相关文章:

c++ - Windows 7 上最快的 IPC 方法

c++ - LR解析时构造AST

c++ - C++11 中的异步 IO

kotlin - 一种有效的方法来锁定Kotlin中对特定资源的访问

java - 当我们已经拥有锁时,再次锁定可重入锁有何帮助?

java - 通用的无锁同步

c++ - 使用字符串在C++中编写代码

c++ - 为什么我不能读取所有 Ctrl + 'letters'

java - 在进程内实现独立通信

c++ - 如何在 C++ 中混合原子和非原子操作?