操作系统:Windows 10编程语言:C ++
我写了一个程序,原本不是多线程的。现在我正在将它固定到多个线程上。
此时它全部围绕一个unordered_map,只有一个线程正在修改地图而所有其他线程只是读取。
所以基本上一次写多次读取。
到目前为止,我已经在写入中实现了mtx.lock和mtx.unlock,并且在写入过程发生时,没有人可以读取。
当有人在阅读时发生WRITE时会出现问题。
据我理解,理论上我可以在每次读取时使用相同的mtx.lock和mtx.unlock,这应该可以解决问题。但这是我迫切想要避免的事情,因为有很多要点读取数据并且我必须实现一百个或更多锁,这对我来说真的很不方便,因为对任何人来说都是如此。
我的问题是:我是否可以让正在进行写作的线程等到每个人都完成阅读,然后调用锁并进行写作。当然,如何才能做到这一点。
听起来你想要的基本上是一个读/写锁。这允许多个读者或单个编写者在任何时候都可以访问,但不能同时访问。
这可以通过使用std::shared_mutex
在C ++ 17中实现,或者(对于较旧的C ++)通过使用boost::shared_mutex
来实现。共享互斥锁允许两个级别的访问,可以是读者请求的共享访问,也可以是您想要写入时请求的独占访问。
“我可以让正在写作的线程等到每个人都完成阅读,然后调用锁并进行写作吗?”
你问的解决方案与你想要实现的目标相反,因为你有多个阅读位置。每次阅读你都必须发送消息(锁定)。
一般来说听起来非常糟糕,但是:如果您的写入偶然发生,您可以向读取线程发送“锁定”消息。当你偶尔写时,阅读线程会收到一条消息/一个事件。然后读取线程会锁定片刻并发送消息(可以写入)写回线程。写完后写线程将通知读取线程可以读取。
最简单的解决方案可能是std::atomic<std::unordered_map*>
。
读取时可以更新原子变量。您不能保证读者是获得旧指针还是新指针,但是它们将获得两个中的一个而不是随机值。
这很容易,但有一个缺点 - 你怎么知道什么时候delete
你已经退休的旧地图是安全的?
还有另一种选择 - std::shared_ptr<std::unordered_map>
。读者在阅读时会制作全局指针的私有副本。这可以确保他们使用的地图版本保持活动状态。编写器线程在有新映射时调用std::make_shared
,并将其分配给全局指针。
std::shared_ptr
足够线程安全。读取它是线程安全的,并且没有冲突的写入器(每个读取器线程都会覆盖它自己的副本。)
防爆。
class Writer {
std::shared_ptr<std::unordered_map<Foo>> m_map;
void add(Foo f) {
auto copy = std::make_shared<std::unordered_map<Foo>>(m_map);
copy.insert(f);
m_map = foo; // thread-safe
}
public:
std::shared_ptr<std::unordered_map<Foo>> getMap() { return m_map; }
};