C++ マルチスレッドライブラリ<thread>
C++11 はマルチスレッドサポートを導入し、<thread>ライブラリを使用すると、開発者はプログラムで簡単に並列処理を実装できます。
この記事では、以下を紹介します。<thread>ライブラリの基本概念、定義、構文、およびそれを使用してスレッドを作成・管理する方法。
スレッドはプログラム実行の最小単位であり、オペレーティングシステムが演算スケジューリングを行える最小の単位です。
マルチスレッドプログラムでは、複数のスレッドが並列に実行でき、プログラムの実行効率を向上させます。
C++ <thread>ライブラリ概要
<thread>ライブラリは C++ 標準ライブラリの一部であり、スレッドを作成および管理するための基本機能を提供します。以下の主要コンポーネントが含まれます:
std::thread: スレッドを表し、スレッドの作成、起動、待機、破棄ができます。std::this_thread: 現在のスレッドを操作するための静的メンバ関数を提供します。std::thread::id: スレッドの一意の識別子です。
スレッドの作成
スレッドを作成するには、インスタンス化する必要がありますstd::threadクラスをインスタンス化し、呼び出し可能オブジェクト(関数、ラムダ式、またはオブジェクトのメンバ関数)を引数として渡します。
例
#include <thread>
void print_id(int id) {
std::cout << "ID: " << id << ", Thread ID: " << std::this_thread::get_id() << std::endl;
}
int main() {
std::thread t1(print_id, 1);
std::thread t2(print_id, 2);
}
スレッドの起動
作成std::threadオブジェクトを作成すると、スレッドは直ちに実行を開始します。次のものを呼び出すことができます。join()メソッドを使用してスレッドの完了を待機します。
t1.join(); t2.join();
スレッドの完了待機
join()メソッドは、呼び出されたスレッドの実行が完了するまで現在のスレッドをブロックします。
スレッドの破棄
スレッドの実行が完了したら、次のものを使用してdetach()メソッドでスレッドを分離するか、またはstd::threadオブジェクトをスコープの外に出して自動的に破棄させることができます。
t1.detach(); // 线程将继续运行,但无法再被 join 或 detach
例:使用<thread>並列計算の作成
以下は、この<thread>ライブラリを使用して実装した並列計算の例で、2つの数値の合計を計算します。
例
#include <thread>
int sum = 0;
void add(int a, int b) {
sum += a + b;
}
int main() {
int a = 5;
int b = 10;
std::thread t1(add, a, b);
std::thread t2(add, a, b);
t1.join();
t2.join();
std::cout << "Sum: " << sum << std::endl; // 出力結果:Sum: 30
}
出力結果は次のとおりです:
Sum: 30
次の例では、2つのスレッドを作成します。各スレッドは、メッセージを出力して一定時間スリープする単純な関数を実行します:
例
#include <thread>
#include <chrono>
// スレッド内で実行される単純な関数
void print_message(const std::string& message, int delay) {
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
std::cout << message << std::endl;
}
int main() {
// 2つのスレッドを作成し、print_message 関数を実行
std::thread t1(print_message, "Hello from thread 1", 1000);
std::thread t2(print_message, "Hello from thread 2", 500);
// スレッド t1 の完了を待機
if (t1.joinable()) {
t1.join();
}
// スレッド t2 の完了を待機
if (t2.joinable()) {
t2.join();
}
std::cout << "Main thread finished." << std::endl;
return 0;
}
出力結果は次のとおりです:
Hello from thread 2 Hello from thread 1 Main thread finished.
注意事項
- スレッドセーフティ:マルチスレッド環境では、データ競合を避けるために共有リソースへのアクセスを同期する必要があります。
- スレッドのライフサイクル:スレッドの実行完了後にスレッドオブジェクトを正しく処理し、リソースリークを防ぐようにしてください。
クラスと関数
<thread>ライブラリには、スレッドの作成、管理、同期に使用する一連のクラスと関数が含まれています。
以下は、C++ の<thread>ライブラリの詳細な説明です:
主要コンポーネント
- std::thread
- std::mutex
- std::lock_guard
- std::unique_lock
- std::condition_variable
- std::future と std::promise
- std::async
std::thread
std::thread クラスはスレッドの作成と管理に使用されます。
例
#include <thread>
void print_hello() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(print_hello);
t.join(); // スレッド t の終了を待機
return 0;
}
重要なメソッド
- join(): スレッドの終了を待機します。
- detach(): スレッドをバックグラウンドで実行し、スレッドの終了を待機しません。
- joinable(): スレッドが join または detach 可能かどうかを確認します。
std::mutex
std::mutex クラスは共有リソースへのアクセスを同期するために使用されます。
例
#include <thread>
#include <mutex>
std::mutex mtx; // グローバルな mutex オブジェクトを作成
int shared_resource = 0; // 共有リソース
// スレッド関数
void increment() {
std::lock_guard<std::mutex> lock(mtx); // ロックしてスレッドセーフを保証
++shared_resource;
std::cout << "Incremented shared_resource to " << shared_resource << std::endl;
// lock_guard がスコープを離れると lock は自動的に解放される
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join(); // スレッド t1 の完了を待機
t2.join(); // スレッド t2 の完了を待機
std::cout << "Final value of shared_resource: " << shared_resource << std::endl;
return 0;
}
std::lock_guard
std::lock_guard は RAII スタイルのロックマネージャであり、ロックのライフサイクルを自動的に管理します。
例
#include <thread>
#include <mutex>
std::mutex mtx;
void print_thread_id(int id) {
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Thread ID: " << id << std::endl;
}
int main() {
std::thread t1(print_thread_id, 1);
std::thread t2(print_thread_id, 2);
t1.join();
t2.join();
return 0;
}
std::unique_lock
std::unique_lock は std::lock_guard よりも柔軟なロック管理を提供します。
例
#include <thread>
#include <mutex>
std::mutex mtx;
void print_thread_id(int id) {
std::unique_lock<std::mutex> lock(mtx);
std::cout << "Thread ID: " << id << std::endl;
lock.unlock(); // 手動でロックを解除できる
// ... その他の操作
}
int main() {
std::thread t1(print_thread_id, 1);
std::thread t2(print_thread_id, 2);
t1.join();
t2.join();
return 0;
}
std::condition_variable
std::condition_variable はスレッド間の待機と通知に使用されます。
例
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
std::cout << "Thread ID: " << id << std::endl;
}
void set_ready() {
std::unique_lock<std::mutex> lock(mtx);
ready = true;
cv.notify_all();
}
int main() {
std::thread t1(print_id, 1);
std::thread t2(print_id, 2);
std::this_thread::sleep_for(std::chrono::seconds(1));
set_ready();
t1.join();
t2.join();
return 0;
}
std::future と std::promise
std::future と std::promise はスレッド間の結果受け渡しに使用されます。
例
#include <thread>
#include <future>
void calculate_square(std::promise<int> && p, int x) {
p.set_value(x * x);
}
int main() {
std::promise<int> p;
std::future<int> f = p.get_future();
std::thread t(calculate_square, std::move(p), 5);
std::cout << "Square: " << f.get() << std::endl;
t.join();
return 0;
}
std::async
std::async は非同期タスクを起動し、std::future を返すために使用されます。
例
#include <future>
int calculate_square(int x) {
return x * x;
}
int main() {
std::future<int> result = std::async(calculate_square, 5);
std::cout << "Square: " << result.get() << std::endl;
return 0;
}
C++ の<thread>ライブラリは開発者に強力なマルチスレッドサポートを提供します。この記事の紹介を通じて、スレッドの基本概念を理解し、次の使用方法を学ぶことができるはずです。<thread>ライブラリを使用してスレッドを作成および管理する方法を学べます。実際の開発では、マルチスレッドを適切に利用することで、プログラムのパフォーマンスと応答速度を大幅に向上させることができます。