【问题标题】:Expression: cannot dereference value-initialized map/set iterator (Debug Assertion Failed!)表达式:无法取消引用值初始化的映射/设置迭代器(调试断言失败!)
【发布时间】:2022-01-21 14:42:09
【问题描述】:

简单代码:

#include <iostream>
#include <random>
#include <string>
#include <map>

std::map<int, uint64_t> mp;

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distr(1, 9);

//std::mutex _mutex;
//std::shared_timed_mutex _mutex;
std::shared_mutex _mutex;

void t1()
{
    int key = 0;
    uint64_t i;
    while (1)
    {
        ++key;
        if (key < 1000)
        {
            std::map<int, uint64_t>::iterator it_mp = mp.find(key);
            if (it_mp != mp.end())
            {
                i = distr(gen);
                std::cout << "UPDATE\t1\t" << it_mp->first << "\t" << it_mp->second << "\t->\t" << i << "\n";
                it_mp->second = i;
            }
            else
            {
                std::unique_lock guard(_mutex);

                i = distr(gen);
                std::cout << "INSERT\t1\t" << key << "\t->\t" << i << "\n";
                mp.insert({ key, i });
            }
        }
        else
        {
            i = distr(gen);
            std::map<int, uint64_t>::iterator it_mp = mp.begin();
            while (it_mp != mp.end())
            {
                if (it_mp->second == i)
                {
                    std::unique_lock guard(_mutex);

                    std::cout << "ERASE\t1\t" << it_mp->first << "\t<-\t" << i << "\n";
                    it_mp = mp.erase(it_mp);
                }
                else
                {
                    ++it_mp;
                }
            }

            key = 0;
        }
    }
}

void t2()
{
    int key = 0;
    uint64_t i;
    while (1)
    {
        ++key;
        if (key < 1000)
        {
            std::map<int, uint64_t>::iterator it_mp = mp.find(key);
            if (it_mp != mp.end())
            {
                i = distr(gen);
                std::cout << "UPDATE\t2\t" << it_mp->first << "\t" << it_mp->second << "\t->\t" << i << "\n";
                it_mp->second = i;
            }
            else
            {
                std::unique_lock guard(_mutex);

                i = distr(gen);
                std::cout << "INSERT\t2\t" << key << "\t->\t" << i << "\n";
                mp.insert({ key, i });
            }
        }
        else
        {
            i = distr(gen);
            std::map<int, uint64_t>::iterator it_mp = mp.begin();
            while (it_mp != mp.end())
            {
                if (it_mp->second == i)
                {
                    std::unique_lock guard(_mutex);

                    std::cout << "ERASE\t2\t" << it_mp->first << "\t<-\t" << i << "\n";
                    it_mp = mp.erase(it_mp);
                }
                else
                {
                    ++it_mp;
                }
            }

            key = 0;
        }
    }
}

int main()
{
    std::thread _t1(t1);
    _t1.detach();

    std::thread _t2(t2);
    _t2.join();
    
    return 0;
}

在任意时间点发生异常(标题错误)。 有没有办法让这段代码以最小的努力工作?

我正在寻找一种在多线程环境中与 std::map 并发通信的方法。但是,Debug Assertion Failed: Expression: cannot dereference value-initialized map/set iterator.

【问题讨论】:

  • 为什么t2 是一个线程?创建一个线程然后直接加入它没有意义。如果你想并行执行t1t2,那么一个 是一个单独的线程就足够了,比如t1。然后你就直接打电话给t2
  • 而且您在没有锁定或同步的情况下使用地图,因此存在数据竞争。这意味着您的代码将具有未定义的行为。您需要保护对地图的所有访问。
  • 这是一个非常大的代码中的一小部分。我一定有成千上万个这样的线程。
  • 我尝试使用 std::unique_lock guard(_mutex); ......我猜没有运气。
  • 您是否尝试过调试它并检查导致异常的原因?更新时不应该锁定地图吗?你不应该在其他操作中更早地锁定地图吗?例如,如果您找到一个密钥并尝试将其擦除,但另一个线程首先将其擦除,会发生什么情况?

标签: c++ insert iterator mutex erase


【解决方案1】:
 std::map<int, uint64_t>::iterator it_mp = mp.find(key);

这会访问多重映射的内容,而不受互斥体的保护。另一个执行线程正在同时修改这个映射。这是未定义的行为。

std::map 和 C++ 库中的任何其他容器都不是线程安全的。这意味着所有对容器的访问,以及不仅仅是对容器的修改,必须同步(即,受互斥体保护)。所示代码锁定互斥锁以仅同步对映射的某些修改。这是不够的。 所有对地图的访问必须同步。

此外,暂时忽略同步主题:整体算法还有另一个缺陷。即使find() 没有在映射中找到现有值,另一个执行线程也可能最终得出相同的结论,并且insert() 在第一个执行线程开始调用insert() 之前将其值放入映射中。所示代码的明确意图是查找/插入是原子操作。这是整个代码部分必须由互斥体保护的另一个原因(不仅是 insert() 调用)。

【讨论】:

    【解决方案2】:

    是的,你是对的。 在迭代器之前需要互斥锁

    shared_lock 在“更新”和“插入”之前,unique_lock 在“擦除”之前。

    翻新代码:

    #include <iostream>
    #include <random>
    #include <string>
    #include <map>
    #include <shared_mutex>
    
    std::map<int, uint64_t> mp;
    
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> distr(1, 9);
    
    //std::mutex _mutex;
    //std::shared_timed_mutex _mutex;
    //std::recursive_mutex _mutex;
    
    std::shared_mutex _mutex;
    
    void t1()
    {
        int key = 0;
        uint64_t i;
        while (1)
        {
            ++key;
            if (key < 1000)
            {
                std::shared_lock guard(_mutex);
                std::map<int, uint64_t>::iterator it_mp = mp.find(key);
                if (it_mp != mp.end())
                {
                    i = distr(gen);
                    std::cout << "UPDATE\t1\t" << it_mp->first << "\t" << it_mp->second << "\t->\t" << i << "\n";
                    it_mp->second = i;
                }
                else
                {
                    i = distr(gen);
                    std::cout << "INSERT\t1\t" << key << "\t->\t" << i << "\n";
                    mp.insert ({key, i});
                }
            }
            else
            {
                i = distr(gen);
                std::unique_lock guard(_mutex);
                std::map<int, uint64_t>::iterator it_mp = mp.begin();
                while (it_mp != mp.end())
                {
                    if (it_mp->second == i)
                    {
                        std::cout << "ERASE\t1\t" << it_mp->first << "\t<-\t" << i << "\n";
                        it_mp = mp.erase(it_mp);
                    }
                    else
                    {
                        ++it_mp;
                    }
                }
    
                key = 0;
            }
        }
    }
    
    void t2()
    {
        int key = 0;
        uint64_t i;
        while (1)
        {
            ++key;
            if (key < 1000)
            {
                std::shared_lock guard(_mutex);
                std::map<int, uint64_t>::iterator it_mp = mp.find(key);
                if (it_mp != mp.end())
                {
                    i = distr(gen);
                    std::cout << "UPDATE\t2\t" << it_mp->first << "\t" << it_mp->second << "\t->\t" << i << "\n";
                    it_mp->second = i;
                }
                else
                {
                    i = distr(gen);
                    std::cout << "INSERT\t2\t" << key << "\t->\t" << i << "\n";
                    mp.insert({ key, i });
                }
            }
            else
            {
                i = distr(gen);
                std::unique_lock guard(_mutex);
                std::map<int, uint64_t>::iterator it_mp = mp.begin();
                while (it_mp != mp.end())
                {
                    if (it_mp->second == i)
                    {
                        std::cout << "ERASE\t2\t" << it_mp->first << "\t<-\t" << i << "\n";
                        it_mp = mp.erase(it_mp);
                    }
                    else
                    {
                        ++it_mp;
                    }
                }
    
                key = 0;
            }
        }
    }
    
    int main()
    {
        std::thread _t1(t1);
        _t1.detach();
    
        std::thread _t2(t2);
        _t2.join();
    
        return 0;
    }
    

    ... 就像魅力一样。 ...也许这会对某人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-20
      • 2014-04-03
      • 1970-01-01
      • 2019-02-08
      • 1970-01-01
      • 2013-06-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多