unordered_map多线程崩溃在find

崩溃

最近程序中出现了崩溃现象,经过查询,发现是崩溃在STL容器的查询中。

崩溃的截图如下:

在这里插入图片描述

关于unordered_map

无序 map 容器,unordered_map 容器不会像 map 容器那样对存储的数据进行排序。

unordered_map 容器底层采用的是哈希表存储结构,该结构本身不具有对数据的排序功能,所以此容器内部不会自行对存储的键值对进行排序。

关联容器删除一个元素的时候,当前的迭代器会失效,其他的迭代器不会失效,增加一个元素的时候,迭代器不会失效。

线程安全性的保证:

  • 多线程同时读
  • 单线程写

也就是说,map容器并不保证读写的线程安全性。

如果一个线程写,同时其他线程读的话,就会存在并发的问题,可能导致崩溃。

测试代码

可以很简单地写一下测试程序:

#include <unordered_map>
#include <map>
#include <future>
#include <string>
#include <vector>
#include <thread>
#include <iostream>
#include <unistd.h>
#include <sys/syscall.h>                 /* For SYS_xxx definitions */

using namespace std;

unordered_map<string, double> test_map;

string code = "000001.SZ";

void write_map()
{
    cout << "write_map start, threadid: " << syscall(SYS_gettid) << endl;

    while (true) {
        test_map[code] = 3;
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
}

void read_map()
{
    cout << "read_map start, threadid: " << syscall(SYS_gettid) << endl;
    double price;
    while (true) {
        auto iter = test_map.find(code);
        if (iter == test_map.end()) {
            cout << "not found|" << endl;
            continue;
        }
        price = iter->second;
    }
}

int main()
{
    vector<thread> threads_;
    for (int i = 0; i < 1; ++i) {
        threads_.emplace_back(std::thread(write_map));
    }
    for (int i = 10; i < 20; ++i) {
        threads_.emplace_back(std::thread(read_map));
    }

    for (std::thread& t : threads_) {
        if (t.joinable()) {
            t.join();
        }
    }

    return 0;
}

编译运行,打开core文件转储,运行几次就得到了core文件。

gdb调试就会看到本文开头的调试信息。

std::shared_mutex

使用mutex可以解决数据竞争的问题,但这会影响系统性能。

而c++17中引入了std::shared_mutex,用于管理可转移和共享所有权的互斥对象。

它适用场景比较特殊:一个或多个读线程同时读取共享资源,且只有一个写线程来写这个资源,这种情况下才能从shared_mutex获取性能优势。

使用std::shared_mutex的示例代码如下:

// from https://en.cppreference.com/w/cpp/thread/shared_mutex
#include <iostream>
#include <mutex>
#include <shared_mutex>
#include <thread>
 
class ThreadSafeCounter {
 public:
  ThreadSafeCounter() = default;
 
  // Multiple threads/readers can read the counter's value at the same time.
  unsigned int get() const {
    std::shared_lock lock(mutex_);
    return value_;
  }
 
  // Only one thread/writer can increment/write the counter's value.
  unsigned int increment() {
    std::unique_lock lock(mutex_);
    return ++value_;
  }
 
  // Only one thread/writer can reset/write the counter's value.
  void reset() {
    std::unique_lock lock(mutex_);
    value_ = 0;
  }
 
 private:
  mutable std::shared_mutex mutex_;
  unsigned int value_ = 0;
};
 
int main() {
  ThreadSafeCounter counter;
 
  auto increment_and_print = [&counter]() {
    for (int i = 0; i < 3; i++) {
      std::cout << std::this_thread::get_id() << ' ' << counter.increment() << '\n';
 
      // Note: Writing to std::cout actually needs to be synchronized as well
      // by another std::mutex. This has been omitted to keep the example small.
    }
  };
 
  std::thread thread1(increment_and_print);
  std::thread thread2(increment_and_print);
 
  thread1.join();
  thread2.join();
}
 
// Explanation: The output below was generated on a single-core machine. When
// thread1 starts, it enters the loop for the first time and calls increment()
// followed by get(). However, before it can print the returned value to
// std::cout, the scheduler puts thread1 to sleep and wakes up thread2, which
// obviously has time enough to run all three loop iterations at once. Back to
// thread1, still in the first loop iteration, it finally prints its local copy
// of the counter's value, which is 1, to std::cout and then runs the remaining
// two loop iterations. On a multi-core machine, none of the threads is put to
// sleep and the output is more likely to be in ascending order.
小结

对于STL容器,线程安全性是需要慎重考虑的地方。

使用锁可以简单地解决这类数据竞争问题。

也可以考虑使用其他方式,如线程安全STL或者boost线程安全容器。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
unordered_mapmap都是C++中的容器,用于存储键值对。然而,它们在多线程环境下的使用有一些区别。 unordered_map是基于哈希表实现的容器,不具有对数据的排序功能。由于哈希表的特性,unordered_map的插入、查找和删除操作的平均时间复杂度都是O(1)。然而,在多线程环境下,对unordered_map进行并发写操作可能会导致数据竞争和错误的结果。因此,如果需要在多线程环境下同时对unordered_map进行写操作,就需要使用互斥锁或其他同步机制来保证线程安全。 相比之下,map是基于红黑树实现的容器,它会对存储的键值对进行排序。map提供了一些线程安全的操作,例如插入和删除操作是安全的,因为它们会在内部进行节点的重新平衡。然而,对于并发的写操作,map并没有内置的线程安全机制。因此,在多线程环境下对map进行并发写操作时,仍然需要使用互斥锁或其他同步机制来确保线程安全。 综上所述,无论是unordered_map还是map,在多线程环境下都需要采取适当的同步机制来避免竞态条件和数据不一致的问题。通过使用互斥锁或其他同步机制,我们可以实现对这两个容器的安全并发访问。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [多线程leetcode-study_resources:学习资源](https://download.csdn.net/download/weixin_38557068/19926672)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [unordered_map多线程崩溃在find](https://blog.csdn.net/guotianqing/article/details/120440508)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值