以下是较大程序的片段,使用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分段故障(核心转储)
如何解决此问题?
答案 0 :(得分:1)
list <string> letterwords[50]
位于UpdaterFunction
的本地。当UpdaterFunction
完成时,其所有局部变量都被破坏。当FunctionMap
检查迭代器时,该迭代器已经指向已删除的内存。
当您插入while(!mapperpool.empty())
UpdaterFunction
等待FunctionMap
完成并letterwords
停留&#39;活着&#39;。
答案 1 :(得分:1)
这里基本上UpdateFunction和FunctionMap在不同的线程上运行。
由于他们都操纵同一个对象(mapperpool
)并且他们都没有使用pthread_mutex和std::mutex(C ++ 11),所以你有一个数据竞争。如果您有数据竞争,则您具有未定义的行为,程序可能会执行任何操作。很可能它会在整个内存中写入垃圾,直到最终崩溃,正如你所看到的那样。
如何解决此问题?
锁定mapperpool
对象。
为什么列表不是线程安全的?
嗯,在绝大多数用例中,单个列表(或任何其他集合)不会被多个线程使用。在其余的很大一部分中,锁必须扩展到集合上的多个操作,因此客户端无论如何都必须自己进行锁定。锁定操作本身有帮助的其余微小百分比不值得为每个人增加开销; C ++关键设计原则是你只需支付你使用的费用。
集合仅可重入,这意味着并行使用不同的实例是安全的。
关于pthreads的说明
C ++ 11引入了threading library,可以很好地与语言集成。最值得注意的是,它使用RAII通过std::mutex,std::lock_guard和std::unique_lock锁定std::shared_lock(用于读写器锁定)。始终如一地使用这些可以消除大量的锁定错误,否则需要花费大量时间进行调试。
如果你还不能使用C ++ 11(在桌面上你可以,但是有些嵌入式平台还没有获得编译器更新),你应该首先考虑Boost.Thread,因为它提供了相同的好处
如果你不能使用,那么仍然试着像C ++ 11 / Boost那样找到或写一个简单的RAII包装器进行锁定。基本的包装器只是几行,但它可以节省大量的调试。