代码之家  ›  专栏  ›  技术社区  ›  user2891462

同步线程安全接口的正确锁定模型

  •  0
  • user2891462  · 技术社区  · 7 年前

    我正在设计一个小图书馆作为玩具项目的一部分。简单来说,应该是这样的:

    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() 返回 福巴 ,不提供 onFoobarCreated() 回拨。
    3. 从不同的角度尽可能独立 对象:不锁定所有对象 福巴 当一个人在发抖的时候。

    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()

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

    1 回复  |  直到 7 年前
        1
  •  1
  •   Paolo Brandoli    7 年前

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

    地图:

    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();
    }
    

    即使从映射中删除Foobar,共享指针也会 findFoobar.second 直到 frobnicate 终止。