来自STL库的列表迭代器出现故障

时间:2016-02-05 08:36:29

标签: c++ stl

以下是较大程序的片段,使用Pthreads完成。

UpdateFunction从文本文件中读取。 FunctionMap仅用于输出(键,1)。这里基本上UpdateFunction和FunctionMap在不同的线程上运行。

queue <list<string>::iterator> mapperpool;

void *UpdaterFunction(void* fn) {
    std::string *x = static_cast<std::string*>(fn);
    string filename = *x;
    ifstream file (filename.c_str());
    string word;
    list <string> letterwords[50];
    char alphabet = '0';
    bool times = true;

    int charno=0;

    while(file >> word) {
        if(times) {
            alphabet = *(word.begin());
            times = false;
        }
        if (alphabet != *(word.begin())) {
            alphabet = *(word.begin());
            mapperpool.push(letterwords[charno].begin());
            letterwords[charno].push_back("xyzzyspoon");
            charno++;
        }
        letterwords[charno].push_back(word);
    }

    file.close();

    cout << "UPDATER DONE!!" << endl;
    pthread_exit(NULL);
}

void *FunctionMap(void *i) {
    long num = (long)i;
    stringstream updaterword;
    string toQ;
    int charno = 0;


    fprintf(stderr, "Print me %ld\n", num);
    sleep(1);

    while (!mapperpool.empty()) {
        list<string>::iterator it = mapperpool.front();
        while(*it != "xyzzyspoon") {
            cout << "(" << *it << ",1)" << "\n";
            cout << *it << "\n";
            it++;   
        }
        mapperpool.pop();
    }

    pthread_exit(NULL);
}

如果我在UpdateFunction中添加while(!mapperpool.empty()),那么它会给我一个完美的输出。但是当我将它移回FunctionMap时,它会给我一个奇怪的东西和后来的Segfaults。 在UpdateFunction中使用时的输出: 打印0 课程 帽 类 文化 类 帽 课程 课程 帽 文化 并发 .....

[单独行中的每个单词]

在FunctionMap中使用时的输出(上面显示的代码段): 打印0 完成UPDATER !! (当然%0 + 0 @ + 0 + 05P + 0cap%+ 0 + 0,05 + 0class5P?0 xyzzyspoon%+ 0 + 0(+ 0%P,0 ,�0�,�05+�0����class%p,�0�,�0-�05�,�0����cap%�,�0�,�0X-�05�,�0����course%-�0 -�0�-�050-�0����course% - 0P-0-05-0cap% - �0�-�0H.�05�-�0����culture%。�0.�0�.�05.�0                                                         并发%P.0`.0.05p.0course%.0.08 / 05.0cap%。 �0/�0�/�05/�0分段故障(核心转储)

如何解决此问题?

2 个答案:

答案 0 :(得分:1)

list <string> letterwords[50]位于UpdaterFunction的本地。当UpdaterFunction完成时,其所有局部变量都被破坏。当FunctionMap检查迭代器时,该迭代器已经指向已删除的内存。 当您插入while(!mapperpool.empty()) UpdaterFunction等待FunctionMap完成并letterwords停留&#39;活着&#39;。

答案 1 :(得分:1)

  

这里基本上UpdateFunction和FunctionMap在不同的线程上运行。

由于他们都操纵同一个对象(mapperpool)并且他们都没有使用pthread_mutexstd::mutex(C ++ 11),所以你有一个数据竞争。如果您有数据竞争,则您具有未定义的行为,程序可能会执行任何操作。很可能它会在整个内存中写入垃圾,直到最终崩溃,正如你所看到的那样。

  

如何解决此问题?

锁定mapperpool对象。

为什么列表不是线程安全的?

嗯,在绝大多数用例中,单个列表(或任何其他集合)不会被多个线程使用。在其余的很大一部分中,锁必须扩展到集合上的多个操作,因此客户端无论如何都必须自己进行锁定。锁定操作本身有帮助的其余微小百分比不值得为每个人增加开销; C ++关键设计原则是你只需支付你使用的费用。

集合仅可重入,这意味着并行使用不同的实例是安全的。

关于pthreads的说明

C ++ 11引入了threading library,可以很好地与语言集成。最值得注意的是,它使用RAII通过std::mutexstd::lock_guardstd::unique_lock锁定std::shared_lock(用于读写器锁定)。始终如一地使用这些可以消除大量的锁定错误,否则需要花费大量时间进行调试。

如果你还不能使用C ++ 11(在桌面上你可以,但是有些嵌入式平台还没有获得编译器更新),你应该首先考虑Boost.Thread,因为它提供了相同的好处

如果你不能使用,那么仍然试着像C ++ 11 / Boost那样找到或写一个简单的RAII包装器进行锁定。基本的包装器只是几行,但它可以节省大量的调试。

请注意C++11Boost也有原子操作库,pthreads非常想念。