[英]C++11 vector argument to thread appears uninitialized
我試圖通過共享狀態的含義創建線程間通信的概念證明:主線程創建工作線程,通過引用為每個工作線程提供單獨的向量,讓每個工作完成其工作並用結果填充其向量,最后收集結果。
然而,奇怪的事情正在發生,除了在向量的初始化和工作線程的啟動之間的某些競爭之外,我找不到解釋。 這是代碼。
#include <iostream>
#include <vector>
#include <thread>
class Case {
public:
int val;
Case(int i):val(i) {}
};
void
run_thread (std::vector<Case*> &case_list, int idx)
{
std::cout << "size in thread " << idx <<": " << case_list.size() << '\n';
for (int i=0; i<10; i++) {
case_list.push_back(new Case(i));
}
}
int
main(int argc, char **argv)
{
int nthrd = 3;
std::vector<std::thread> threads;
std::vector<std::vector<Case*>> case_lists;
for (int i=0; i<nthrd; i++) {
case_lists.push_back(std::vector<Case*>());
std::cout << "size of " << i << " in main:" << case_lists[i].size() << '\n';
threads.push_back( std::thread( run_thread, std::ref(case_lists[i]), i) );
}
std::cout << "All threads lauched.\n";
for (int i=0; i<nthrd; i++) {
threads[i].join();
for (const auto cp:case_lists[i]) {
std::cout << cp->val << '\n';
}
}
return 0;
}
在repl.it (gcc 4.6.3)上測試,程序給出以下結果:
size of 0 in main:0
size of 1 in main:0
size of 2 in main:0
All threads lauched.
size in thread 0: 18446744073705569740
size in thread 2: 0
size in thread 1: 0
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
exit status -1
在我的電腦上,除了上面的內容之外,我還得到:
Segmentation fault (core dumped)
看起來線程0正在獲得一個尚未初始化的向量,盡管向量在main中正確初始化。
為了隔離問題,我嘗試通過更改行來進行單線程:
threads.push_back( std::thread( run_thread, std::ref(case_lists[i]), i) );
至
run_thread(case_lists[i], i);
和評論:
threads[i].join();
現在程序按預期運行,在主要收集結果之前,“線程”一個接一個地運行。
我的問題是:上面的多線程版本出了什么問題?
只要vector
的容量發生變化, vector
引用(和迭代器)就會失效。 確切的分配規則因實現而異,但可能性是,您在第一個push_back
和最后一個之間至少有一個容量變化,並且在最終容量增加之前所做的所有引用在它發生時都是垃圾,調用未定義的行為。
預先reserve
總vector
大小(因此push_back
不會導致容量增加),將整個vector
初始化為最終大小(所以根本沒有調整大小),或者完全填充一個循環, 然后啟動線程(因此所有調整大小都會在您提取任何引用之前發生)。 這里最簡單的解決方法是將其初始化為最終大小,更改:
std::vector<std::vector<Case*>> case_lists;
for (int i=0; i<nthrd; i++) {
case_lists.push_back(std::vector<Case*>());
std::cout << "size of " << i << " in main:" << case_lists[i].size() << '\n';
threads.push_back( std::thread( run_thread, std::ref(case_lists[i]), i) );
}
至:
std::vector<std::vector<Case*>> case_lists(nthrd); // Default initialize nthrd elements up front
for (int i=0; i<nthrd; i++) {
// No push_back needed
std::cout << "size of " << i << " in main:" << case_lists[i].size() << '\n';
threads.push_back( std::thread( run_thread, std::ref(case_lists[i]), i) );
}
您可能認為vector
s會相當積極地進行,但至少在許多流行的編譯器上,情況並非如此; gcc和clang都遵循嚴格的加倍模式,因此前三次插入每次重新分配(容量從1到2,再到4); 插入第二個元素對第一個元素的引用無效,第二個元素的引用通過插入第三個元素而無效。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.