簡體   English   中英

線程的C ++ 11向量參數顯示為未初始化

[英]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和最后一個之間至少有一個容量變化,並且在最終容量增加之前所做的所有引用在它發生時都是垃圾,調用未定義的行為。

預先reservevector大小(因此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會相當積極地進行,但至少在許多流行的編譯器上,情況並非如此; gccclang都遵循嚴格的加倍模式,因此前三次插入每次重新分配(容量從1到2,再到4); 插入第二個元素對第一個元素的引用無效,第二個元素的引用通過插入第三個元素而無效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM