如何从多个线程并行安全地访问和写入复杂容器?
How to safely access and write to a complex container from multiple threads in parallel?
我有一个结构unordered_map的情况。该结构包含 int、布尔值和向量。我的程序将通过对服务器的https调用或使用websocket获取映射中每个项目的数据(map中的每个项目都需要单独的https调用(。使用 websocket 时,将一起返回地图中所有项目的数据。获取的数据被处理并存储在相应的向量中。
websocket 在单独的线程中运行,应该在程序的整个生命周期内运行。
我的程序有一个删除功能,可以"清空"整个地图。还有一个addItem((函数,它将向我的map添加新结构。
每当结构的 "updatesOn" 成员为 false 时,就不会将任何数据推送到向量中。
我当前的实现有 3 个线程:
- 主线程将向地图添加新项目。主线程的另一个功能是从结构中的向量获取数据。主线程具有清空映射并重新开始的功能。它还有另一个只清空向量的函数。
- 第二个线程将运行 WebSocket 客户端,并在新数据到达时填充结构中的 Vector。有一个 while 循环检查退出标志。在主线程中设置退出标志后,此线程将终止。
- 第三个线程是管理器线程。它在map中查找新条目并进行http下载,然后将此项添加到websocket以进行后续数据更新。它还定期运行 http 下载,清空矢量并重新填充它。
现在我有两个互斥锁。
- 一个用于在向量写入/读取数据之前锁定。
- 第二个互斥锁是指在映射中添加或删除新数据。也可在清空地图时使用。
我觉得这是互斥锁的错误用法。正如我可能会在读取或写入其结构的矢量元素之一时清空地图。这使我对所有人使用一个互斥锁。
问题是这是一个实时股票数据程序,即每秒都会弹出新数据,有时甚至更快。恐怕一个互斥锁可能会减慢我的整个应用程序的速度。
如上所述,所有 3 个线程都对此映射具有写入访问权限,主线程能够将其清空完成。
牢记速度和线程安全,实现这一点的好方法是什么?
我的数据成员:
unordered_map<string, tickerDiary> tDiaries;
struct tickerDiary {
tickerDiary() : name(""), ohlcPeriodicity("minute"), ohlcStatus(false), updatesOn(true), ohlcDayBarIndex(0), rtStatus(false) {}
string name;
string ohlcPeriodicity;
bool ohlcStatus;
bool rtStatus;
bool updatesOn;
int32 ohlcDayBarIndex;
vector<Quotation> data;
};
struct Quotation {
union AmiDate DateTime;
float Price;
float Open;
float High;
float Low;
float Volume;
float OpenInterest;
float AuxData1;
float AuxData2;
};
注意:我使用的是C++11。
如果我正确理解您的问题,您的地图本身主要编写在主线程中,其他线程仅用于对映射条目中包含的数据进行操作。
鉴于此,对于非主线程,有两个问题:
- 他们处理的项目不应随机消失
- 他们应该是唯一一个处理他们的项目的人。
第一个问题可以通过将存储与映射分离来最有效地解决。因此,对于每个项目,存储是单独分配的(通过默认分配器,或者如果您添加/删除项目很多,则通过某些池化方案(,并且地图仅存储共享 ptr。然后,处理项目的每个线程只需要保留一个共享的 ptr,以确保存储不会从它们下面消失。然后,仅在获取/存储/删除指针期间需要获取映射的关联互斥锁/shared_mutex。然后,只要可以接受某些线程可能会浪费一些时间对已从地图中删除的项目执行操作,就可以正常工作。使用 shared_ptrs 将确保您不会通过使用引用计数器泄漏内存,并且它们还将对这些引用计数进行锁定/解锁(或者更确切地说,尝试为这些引用使用更有效的平台原语(。如果你想了解更多关于shared_ptr和一般的智能指针,这是对智能指针的 C++ 系统的合理介绍。
这就留下了第二个问题,这个问题可能最容易通过在数据结构(tickerDiary(本身中保留互斥锁来解决,线程在开始执行需要结构中可预测行为的操作时获取该问题,并且可以在完成它们应该做的事情后释放。
以这种方式分离锁定应该可以减少映射的全局锁上的争用量。但是,您可能应该对您的代码进行基准测试,看看考虑到单个项目的分配和引用计数的额外成本,这种减少是否值得。
我认为使用std::vector
不是正确的集合。但是,如果您坚持使用它,则每个集合应该只有一个互斥锁。
我建议使用英特尔 TBB 的concurrent_vector
或 boost 的同步数据结构。
第三种解决方案可能是实现自己的并发向量
- 通过方法访问结构
- 使用不带参数的函数访问结构元素
- 如果我只是不访问queue_front节点的子节点,而是将它们推到队列中呢?还是BFS吗
- 用于访问容器<T>数据成员的正确 API
- 访问者访问变体并返回不同类型时出错
- 尝试通过多个向量访问变量时,向量下标超出范围
- 无法访问嵌套类.类的使用无效
- 写入位置0x0000000C时发生访问冲突
- 函数复杂度分析
- 我们可以访问一个不存在的联盟的成员吗
- C++从另一个类访问公共静态向量的正确方法是什么
- 如何在复杂继承中访问静态成员变量
- Deque 中元素的随机访问如何提供恒定的时间复杂度?
- 如何从多个线程并行安全地访问和写入复杂容器?
- 如何通过 COM 互操作访问复杂对象的属性?
- DFS 访问内部的地图元素.时间复杂度
- 是否应该对复杂数据类型使用访问器
- 访问受保护的变量 - 继承和子类的复杂情况
- 如何简化复杂的API访问(v8)
- 对于boost::hana::tuple,元素访问的时间复杂度是多少?