std::vector を mutex でロックしてマルチスレッドで書き込む

C++ の std::thread でひとつの vector に push_back しまくるコード

#include <iostream>
#include <vector>
#include <thread>

int main() {
    std::vector<int> vi;
    std::vector<std::thread> vt;

    for(int i=0; i<100; ++i) {
        vt.push_back(std::thread([&i, &vi]{ vi.push_back(i); }));
    }
    for(int i=0; i<100; ++i) {
        vt[i].join();
    }

    return 0;
}

これをコンパイルして実行すると

$ g++ test.cpp -pthread
$ ./a.out
(...)
Aborted (core dumped)

当然こうなる。 というわけで mutex を使って vector をロックするようにする。正確には、vector をメンバとして持ち、ロックもできる class をつくる。

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>

class myvector {
    std::mutex mtx;
    std::vector<int> data;
public:
    void push_back(int x) {
        std::lock_guard<std::mutex> lock(mtx);
        data.push_back(x);
    }
    void print() {
        for(int x: data) {
            std::cout << x << '\n';
        }
    }
};

int main() {
    const int N = 100;

    std::vector<std::thread> vt;
    myvector mv;

    for(int i=0; i<N; i++) {
        vt.push_back(std::thread([=, &mv]{ mv.push_back(i); }));
    }
    for(int i=0; i<N; i++) {
        vt[i].join();
    }

    mv.print();
}

これで 0 〜 99 までの数字が出力される。順番が正確に 0 〜 99 ではないのは並列化のため。

なお最初ラムダ式のところをうっかり

vt.push_back(std::thread([&i, &mv]{ mv.push_back(i); }));

と書いていたが、これだと i がコピーではなく参照になってしまうので、 mv に入る i の値が正確に 0〜99 でなくなってしまった(たいてい 2 から始まる)。ラムダ式に対し意味もわからず「使う変数を & で書いとくんやろ」という甘い考えを持っていたことを反省。