多个线程同时在 unordered_map 中添加值使其崩溃

Posted

技术标签:

【中文标题】多个线程同时在 unordered_map 中添加值使其崩溃【英文标题】:Multiple threads adding values in a unordered_map at the same time makes it crash 【发布时间】:2012-04-07 18:33:09 【问题描述】:
unordered_map<std::string,unordered_map<std::string, std::string> >* storing_vars;

我在作用域中声明了这个变量。

这是在构造函数中声明的。

this->storing_vars =  new unordered_map<std::string,unordered_map<std::string, std::string> >();

为了初始化它。

然后我所做的就是由我的 BackgroundWorker 一遍又一遍地调用一个函数

for(int i2 = 0; i2 < 30; i2++)



                int index_pos_curr = i2;


                //Start the Threads HERE


                this->backgroundWorker2 = gcnew System::ComponentModel::BackgroundWorker;
                this->backgroundWorker2->WorkerReportsProgress = true;
                this->backgroundWorker2->WorkerSupportsCancellation = true;


                //this->backgroundWorker2->FieldSetter(L"std::string",L"test","damnnit");

                backgroundWorker2->DoWork += gcnew DoWorkEventHandler( this, &MainFacebook::backgroundWorker2_DoWork );
                backgroundWorker2->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler( this, &MainFacebook::backgroundWorker2_RunWorkerCompleted );
                backgroundWorker2->ProgressChanged += gcnew ProgressChangedEventHandler( this, &MainFacebook::backgroundWorker2_ProgressChanged );
                backgroundWorker2->RunWorkerAsync(index_pos_curr);
                Sleep(50); //THE PROBLEM IS HERE, IF I COMMENT THIS OUT it won't work, that's probably because there are a lot of functions trying to add values in the same variable (even though the indexes are differents in each call)
            

完成此操作后,它会调用 DoWork 函数

void backgroundWorker2_DoWork(Object^ sender, DoWorkEventArgs^ e )
             BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);         
             e->Result = SendThem( safe_cast<Int32>(e->Argument), worker, e );          

        

int SendThem(int index)

            stringstream st;
            st << index;

            //...
            (*this->storing_vars)[st.str()]["index"] =  "testing1";
            (*this->storing_vars)[st.str()]["rs"] = "testing2";
            return 0;

当我在Sleep(50) 行中添加注释时,我认为问题在于,由于后台线程调用相同的函数,因此在多次调用时存储数据存在问题,甚至可能没有等待其他存储完成,这会导致“xhash.h”文件中出现错误,该错误已通过使用Sleep(50) 进行清理,但我无法使用这些,因为它会冻结我的 UI,而且 50 毫秒是时间我假设它已经存储了变量值,但是如果在较慢的计算机上需要更长的时间呢?这不是正确的方法。

我该如何解决这个问题?

我希望能够在不使用 SLEEP 的情况下更新 unordered_map

提前致谢。

【问题讨论】:

总结一下问题是因为您试图从两个不同的线程更新相同的 unordered_map 是否公平。您正试图通过添加睡眠来修复它?是这个问题吗? 你的同步机制在哪里?我没有看到.... 不,我不想使用睡眠,我是说睡眠是我发现不会导致该问题的唯一方法,我希望它能够更新相同的 unordered_map 并且同时由不同的线程再次重复,而不会在文件“xhash”中抛出错误(可能来自 unordered_map 标头),所以我不想让它睡觉我希望它在没有睡眠的情况下工作 @Grego :互斥体或临界区。您在多线程上下文中使用线程不安全的类,您期望会发生什么? @Grego:那是你的问题。你会在不知道方向盘是什么的情况下开车吗?你需要知道你在做什么。这包括知道什么是互斥锁和临界区,这意味着知道你正在使用的数据结构提供了什么(如果有的话)多线程保证 【参考方案1】:

您一次只能从一个线程修改标准库容器(包括但不限于unordered_map)。解决方案是使用临界区、互斥体、锁来同步访问。如果您不知道这些是什么,那么您需要在尝试创建多个线程之前了解

没有如果、但是或为什么。

如果您有多个线程,则需要同步它们的机制,以序列化对共享数据的访问。常见的同步机制就是上面提到的那些,你去看看吧。

【讨论】:

抱歉,这样不行。如果不了解如何制定可行的解决方案,您就无法制定可行的解决方案。在截止日期前并不会改变这一点。这只是意味着你不负责任,并且会让你的老板/老师/任何事情失望 @Grego:它只是在特定的时间点工作,它可能会在明天更冷或更热时停止工作,或者在另一台计算机上,或者当你正在听不同的音乐时。这是您不了解的,但您需要了解才能使其发挥作用。 @Grego :使用搜索引擎。 这在各种文章/书籍/教程中都有上千次的解释。表现出一点努力。 @Grego 问题在于这不是一个简单的问题。多线程和同步基本上是编程中最复杂的主题之一,您要求在评论中向您解释。至少,为此发布一个新问题。 @Grego:不,你用 AJAX 做的不是多线程。这是一个调用异步函数的线程。你可以在 C++ 中做同样的事情(它比实际拥有多个线程要简单和安全得多)。但是,在您的情况下,最简单的解决方案是只使用一个线程,并同步执行每个操作。希望有帮助。 :)【参考方案2】:

经过这么多票否决后,我实际上开始寻找互斥锁,人们在这里谈论,过了一段时间我发现它真的很容易使用。这是我的同事告诉我的正确方法。谢谢大家的帮助=D

这是我所做的,我只需要添加

//Declare the Mutex
static Mutex^ mut = gcnew Mutex;

//then inside the function called over and over again I used mutex
mut->WaitOne();
//Declare/Update the variables
mut->ReleaseMutex();
//Then I release it.

效果很好,谢谢大家的帮助和批评。哈哈

【讨论】:

哈哈我不明白@sbi,你在说什么“OhMyGod”? '-'【参考方案3】:

我通过预定义我想使用的 unordered_map 的索引找到了一种解决方案,问题只是创建索引,多线程更新似乎没问题。

for(int i2 = 0; i2 < 30; i2++)



                int index_pos_curr = i2;


                //Start the Threads HERE


                this->backgroundWorker2 = gcnew System::ComponentModel::BackgroundWorker;
                this->backgroundWorker2->WorkerReportsProgress = true;
                this->backgroundWorker2->WorkerSupportsCancellation = true;
                backgroundWorker2->DoWork += gcnew DoWorkEventHandler( this, &MainFacebook::backgroundWorker2_DoWork );
                backgroundWorker2->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler( this, &MainFacebook::backgroundWorker2_RunWorkerCompleted ); stringstream st; st << index_pos_curr;

                (*this->storing_vars)[st.str()]["index"] = ""; 
               //This ^^^^^ will initialize it and then in the BackgroundWorker will only update, this way it doesn't crash. :)

                backgroundWorker2->ProgressChanged += gcnew ProgressChangedEventHandler( this, &MainFacebook::backgroundWorker2_ProgressChanged );
                backgroundWorker2->RunWorkerAsync(index_pos_curr);
                Sleep(50); //THE PROBLEM IS HERE, IF I COMMENT THIS OUT it won't work, that's probably because there are a lot of functions trying to add values in the same variable (even though the indexes are differents in each call)
            

【讨论】:

只是....尝试理解问题,而不是随机修改代码直到似乎可以工作 我做了,我在cmets中解释过,我说问题是变量是同时创建的,所以如果我之前创建它,它已经有一个内存地址然后它只需要存储在该地址中而不创建另一个,同时创建多个内存可能会导致错误,具体取决于它进入内存的速度或程序发送命令的速度。 你仍然调用Undefined Behavior,只是现在它表现为看似在做你想让代码做的事情。不幸的是,这是 UB 表现自己的一种方式。更不幸的是,它可能会无缘无故地突然开始做其他事情。 Thus is the nature of UB.

以上是关于多个线程同时在 unordered_map 中添加值使其崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Map vs Unordered_map——多线程

如何在 C++ 中的 unordered_map 的并发读取和锁定单线程写入之间交替

多个线程同时在同一向量的不同向量上添加元素发生错误

如何在添加和删除元素时转储无序地图?

如何在 C++ 中单独锁定 unordered_map 元素

多个线程同时向不同步的ArrayList的对象添加元素可能会导致哪些问题?