【问题标题】:How do I use std::lock_guard to lock read and write access to a std::map?如何使用 std::lock_guard 锁定对 std::map 的读写访问?
【发布时间】:2012-08-19 12:26:27
【问题描述】:

当我在 MSVC 2012 中逐步执行以下代码时 我在第 3 行和第 8 行放置了一个断点。

第一个中断出现在第 8 行。

lock_guard 被调用得很好,然后我们在第 3 行中断。 这一次,由于已经获得了锁,所以当我跨步时会抛出异常。

我真的希望它继续前进,因为它仍然是同一个线程调用(我们刚从第 11 行来。)

还有其他更适合这种情况的锁定机制吗?

我有本地 win32 编程的背景,所以我习惯于 WaitForSingleObject,它会让我轻松通过这里, 但 lock_guard 没有。

我应该处理异常吗?我见过的所有示例都没有任何类型的 lock_guard 异常处理程序......

有没有更好的方法来确保地图一次不会被多个线程访问? 我需要对其进行读写锁定,而 lock_guard 似乎是一个不错的选择,因为我不需要 ReleaseMutex...

    //Cars.h
    mutable std::mutex carsMutex;
    class Cars
    {
    private:
        std::map<std::string,Cars> _cars;
    public:
        virtual ~Cars() {}
        Cars() {}
        Cars & operator[](const std::string &key);
        Cars & operator[](const DWORD &key);
        std::string color;
    };

    //Cars.cpp
      #include "Cars.h"
1.    Cars & Cars::operator[](const std::string &key)
2.    {
3.        std::lock_guard<std::mutex> lock_a(carsMutex);
4.        return _cars[key];
5.    }
6.    Cars & Cars::operator[](const DWORD &key)
7.    {
8.        std::lock_guard<std::mutex> lock_a(carsMutex);
9.        std::stringstream ss;
10.       ss << key;
11.       return operator[](ss.str());
12.   }
14.   void main()
15.   {
16.       //ok i have multiple threads like this one writing and reading from the map
17.       Cars cars;
18.       cars[(DWORD)2012]["volvo"].color = "blue";
19.   }

更新: 这是我对上面代码的编辑。 我已经考虑了答案,这是我正确使用 std::lock_guard 的新尝试 如有不妥请评论。

    //Cars.h
    mutable std::recursive_mutex carsMutex;
    class Cars
    {
    private:
        std::string _color;
        std::map<std::string,Cars> _cars;
    public:
        virtual ~Cars() {}
        Cars() {}
        Cars & operator[](const std::string &key);
        Cars & operator[](const DWORD &key);
        void color(const std::string &color);
        std::string color();
    };

       //Cars.cpp
       #include "Cars.h"
 1.    Cars & Cars::operator[](const std::string &key)
 2.    {
 3.        std::lock_guard<std::recursive_mutex> lock(carsMutex);
 4.        return _cars[key];
 5.    }
 6.    Cars & Cars::operator[](const DWORD &key)
 7.    {
 8.        std::lock_guard<std::recursive_mutex> lock(carsMutex);
 9.        std::stringstream ss;
10.        ss << key;
11.        return operator[](ss.str());
12.    }
13.    void color(const std::string &color)
14.    {
15.        std::lock_guard<std::recursive_mutex> lock(carsMutex);
16.        _color = color;
17.    }
18.    std::string color()
19.    {
20.        std::lock_guard<std::recursive_mutex> lock(carsMutex);
21.        return _color;
22.    }
23.
24.    Cars cars;//this is global...
25.    void main()
26.    {
27.        //ok i have multiple threads like this one writing and reading from the map
28.        cars[(DWORD)2012]["volvo"].color("blue");
29.    }

【问题讨论】:

    标签: c++ exception c++11 native mutex


    【解决方案1】:

    要允许线程重新获取它已经拥有的互斥锁,您需要std::recursive_mutex 而不是std::mutex

    但是,您有一个更大的问题:您的访问器在 返回您用于分配地图元素的引用之前解锁互斥锁,因此分配本身是无人看管的。这可以通过编写一个在返回之前执行赋值的“setter”函数来解决;或者通过返回包含在其析构函数中释放的互斥锁的代理对象。解决这个问题可能会消除对recursive_mutex 的需求。

    【讨论】:

    • 好吧,这听起来很有希望,但后来我收到了这个错误:错误 C2664: 'std::lock_guard<_mutex>::lock_guard(_Mutex &)' : cannot convert parameter 1 from 'std::recursive_mutex ' to 'std::mutex &' 我应该直接转换它吗?
    • @user1621065:你有没有把锁守卫改成lock_guard&lt;recursive_mutex&gt;?您是否阅读了我更新的答案,它解释了您认为需要递归互斥体背后的更深层次的问题?
    • 好的 :) 谢谢,这么简单,但我无法弄清楚,除此之外,如果另一个线程在锁定时尝试获取锁,它会抛出异常还是等到锁打开然后进入?
    • @user1621065:是的,但是您返回一个引用,然后在释放锁后写入该引用,而其他一些线程也可能试图访问同一个地图元素。您需要保持互斥锁锁定,直到整个操作完成。
    • @user1621065:正如这个基本错误所示,你的锁放错了地方。您正在锁定 map 操作,而您想要锁定的是地图中 Cars 上的操作。所以把你的锁移到他们应该保护的操作完成的地方。 (或者,如果绝对必要,让map 为更高级别的调用者提供“给我一个锁持有者对象”功能。但这是次要的。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-18
    • 2022-11-22
    相关资源
    最近更新 更多