スレッドの作成
C++標準ライブラリは、std::threadクラスを通じてマルチスレッドプログラミングをサポートしています。このクラスを使用すると、新しいスレッドを簡単に作成し、管理することができます。
std::threadオブジェクトをインスタンス化することで、新しいスレッドを起動できます。コンストラクタには、実行される関数や関数オブジェクトを渡します。
#include <iostream>
#include <thread>
#include <chrono>
// ラムダ式を使用したスレッドの作成
std::thread taskA([]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "タスクAが実行されました。" << std::endl;
});
std::thread taskB([]() {
std::cout << "タスクBが実行されました。" << std::endl;
});
// スレッドの終了を待機
taskA.join();
taskB.join();
return 0;
この例では、2つのラムダ式がそれぞれ異なるスレッドで実行されます。join()メソッドは、対応するスレッドが終了するまでmain関数の実行をブロックします。
スレッド関数の定義
スレッド関数には、関数ポインタ、関数オブジェクト、ラムダ式など、呼び出し可能なあらゆるオブジェクトを使用できます。スレッド関数は、引数を受け取ることもできます。
#include <iostream>
#include <thread>
#include <vector>
// スレッド関数
void processData(int taskNumber) {
std::cout << "データ処理 " << taskNumber << " を開始します。" << std::endl;
// ここに処理ロジックを記述
std::cout << "データ処理 " << taskNumber << " が完了しました。" << std::endl;
}
int main() {
std::vector<std::thread> threads;
// 複数のスレッドを作成
for (int i = 1; i <= 3; ++i) {
threads.emplace_back(processData, i);
}
// すべてのスレッドの終了を待機
for (auto& t : threads) {
t.join();
}
return 0;
}
上記のコードでは、processDataという名前の関数をスレッド関数として使用し、ループ内で複数のスレッドを作成しています。std::vectorを使用してスレッドオブジェクトを管理し、最後にすべてのスレッドが終了するのを待っています。
スレッドの同期と排他制御
複数のスレッドが同じデータやリソースに同時にアクセスすると、データ競合が発生し、予期しない動作の原因となります。この問題を防ぐために、スレッド間の同期と排他制御が必要です。
C++では、std::mutex(ミューテックス)やstd::atomic(アトミック操作)などの機構が提供されています。std::mutexは、特定のコードブロックを一度に一つのスレッドだけが実行できるように保護します。std::lock_guardを使用すると、ロックの取得と解放を自動的に管理でき、例外安全なコードを書くことができます。
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
std::mutex mtx;
int sharedCounter = 0;
void incrementCounter(int id) {
// std::lock_guardを使用してミューテックスを自動的に管理
std::lock_guard<std::mutex> lock(mtx);
std::cout << "スレッド " << id << " がカウンタをインクリメントします。" << std::endl;
++sharedCounter;
std::cout << "現在のカウンタ値: " << sharedCounter << std::endl;
}
int main() {
std::vector<std::thread> threads;
// 複数のスレッドを作成
for (int i = 1; i <= 5; ++i) {
threads.emplace_back(incrementCounter, i);
}
// すべてのスレッドの終了を待機
for (auto& t : threads) {
t.join();
}
std::cout << "最終的なカウンタ値: " << sharedCounter << std::endl;
return 0;
}
この例では、sharedCounterという共有変数を複数のスレッドがインクリメントします。std::mutexとstd::lock_guardを用いることで、同時に一つのスレッドしかsharedCounterを変更できないように保護しています。これにより、データ競合を防ぎ、正しい結果を保証します。