サーチ…
構文
- 糸()
- スレッド(スレッド&&その他)
- 明示的なスレッド(関数&& func、Args && ... args)
パラメーター
パラメータ | 詳細 |
---|---|
other | other 所有権を引き継ぎ、 other はもうスレッドを所有していない |
func | 別のスレッドで呼び出す関数 |
args | func 引数 |
備考
いくつかのメモ:
- 2つの
std::thread
オブジェクトは、同じスレッドを表すことはできません 。 -
std::thread
オブジェクトは、スレッドを表現していない状態(移動後、join
呼び出し後など)になることがあります。
スレッド操作
スレッドを開始すると、スレッドが終了するまで実行されます。
多くの場合、結果を使用したいので、スレッドが完了するのを待つ必要があります(おそらく、スレッドがすでに終了している可能性があります)。
int n;
std::thread thread{ calculateSomething, std::ref(n) };
//Doing some other stuff
//We need 'n' now!
//Wait for the thread to finish - if it is not already done
thread.join();
//Now 'n' has the result of the calculation done in the seperate thread
std::cout << n << '\n';
スレッドをdetach
、スレッドを自由に実行させることもできます。
std::thread thread{ doSomething };
//Detaching the thread, we don't need it anymore (for whatever reason)
thread.detach();
//The thread will terminate when it is done, or when the main thread returns
スレッドへの参照の受け渡し
std::thread
はそれらをコピー/移動するので、参照(またはconst
参照)をスレッドに直接渡すことはできません。代わりに、 std::reference_wrapper
使用しstd::reference_wrapper
。
void foo(int& b)
{
b = 10;
}
int a = 1;
std::thread thread{ foo, std::ref(a) }; //'a' is now really passed as reference
thread.join();
std::cout << a << '\n'; //Outputs 10
void bar(const ComplexObject& co)
{
co.doCalculations();
}
ComplexObject object;
std::thread thread{ bar, std::cref(object) }; //'object' is passed as const&
thread.join();
std::cout << object.getResult() << '\n'; //Outputs the result
std :: threadの作成
C ++では、スレッドはstd :: threadクラスを使用して作成されます。スレッドとは別の実行フローです。他のタスクを同時に実行している間にヘルパーに1つのタスクを実行させることに似ています。スレッド内のすべてのコードが実行されると、 終了します 。スレッドを作成するときには、実行するものを渡す必要があります。あなたがスレッドに渡すことができるいくつかの事柄:
- フリー関数
- メンバー関数
- Functorオブジェクト
- ラムダ式
フリー関数の例 - 別のスレッドで関数を実行する( 実例 ):
#include <iostream>
#include <thread>
void foo(int a)
{
std::cout << a << '\n';
}
int main()
{
// Create and execute the thread
std::thread thread(foo, 10); // foo is the function to execute, 10 is the
// argument to pass to it
// Keep going; the thread is executed separately
// Wait for the thread to finish; we stay here until it is done
thread.join();
return 0;
}
メンバー関数の例 - 別のスレッドでメンバー関数を実行する( 実例 ):
#include <iostream>
#include <thread>
class Bar
{
public:
void foo(int a)
{
std::cout << a << '\n';
}
};
int main()
{
Bar bar;
// Create and execute the thread
std::thread thread(&Bar::foo, &bar, 10); // Pass 10 to member function
// The member function will be executed in a separate thread
// Wait for the thread to finish, this is a blocking operation
thread.join();
return 0;
}
Functorオブジェクトの例( 実例 ):
#include <iostream>
#include <thread>
class Bar
{
public:
void operator()(int a)
{
std::cout << a << '\n';
}
};
int main()
{
Bar bar;
// Create and execute the thread
std::thread thread(bar, 10); // Pass 10 to functor object
// The functor object will be executed in a separate thread
// Wait for the thread to finish, this is a blocking operation
thread.join();
return 0;
}
ラムダ式の例( 実例 ):
#include <iostream>
#include <thread>
int main()
{
auto lambda = [](int a) { std::cout << a << '\n'; };
// Create and execute the thread
std::thread thread(lambda, 10); // Pass 10 to the lambda expression
// The lambda expression will be executed in a separate thread
// Wait for the thread to finish, this is a blocking operation
thread.join();
return 0;
}
現在のスレッドでの操作
std::this_thread
は、呼び出された関数から現在のスレッド上で興味深いことをする関数を持つnamespace
です。
関数 | 説明 |
---|---|
get_id | スレッドのIDを返します。 |
sleep_for | 指定された時間だけ眠る |
sleep_until | 特定の時間まで眠る |
yield | 実行中のスレッドを再スケジュールし、他のスレッドに優先順位を与える |
std::this_thread::get_id
を使って現在のスレッドIDを取得する:
void foo()
{
//Print this threads id
std::cout << std::this_thread::get_id() << '\n';
}
std::thread thread{ foo };
thread.join(); //'threads' id has now been printed, should be something like 12556
foo(); //The id of the main thread is printed, should be something like 2420
std::this_thread::sleep_for
を使って3秒間std::this_thread::sleep_for
:
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
}
std::thread thread{ foo };
foo.join();
std::cout << "Waited for 3 seconds!\n";
将来3時間までstd::this_thread::sleep_until
を使用してstd::this_thread::sleep_until
:
void foo()
{
std::this_thread::sleep_until(std::chrono::system_clock::now() + std::chrono::hours(3));
}
std::thread thread{ foo };
thread.join();
std::cout << "We are now located 3 hours after the thread has been called\n";
他のスレッドにstd::this_thread::yield
を使って優先さstd::this_thread::yield
:
void foo(int a)
{
for (int i = 0; i < al ++i)
std::this_thread::yield(); //Now other threads take priority, because this thread
//isn't doing anything important
std::cout << "Hello World!\n";
}
std::thread thread{ foo, 10 };
thread.join();
std :: threadの代わりにstd :: asyncを使う
std::async
はスレッドを作成することもできます。 std::thread
と比較すると、あまり強力ではありませんが、非同期的に関数を実行したいときには使いやすくなります。
非同期に関数を呼び出す
#include <future>
#include <iostream>
unsigned int square(unsigned int i){
return i*i;
}
int main() {
auto f = std::async(std::launch::async, square, 8);
std::cout << "square currently running\n"; //do something while square is running
std::cout << "result is " << f.get() << '\n'; //getting the result from square
}
共通落とし穴
std::async
は、関数によって計算される戻り値を保持するstd::future
を返します。そのfuture
が破壊されると、スレッドが完了するまで待って、コードを効果的にシングルスレッドにします。これは、戻り値が不要なときに簡単に見落とされます。std::async(std::launch::async, square, 5); //thread already completed at this point, because the returning future got destroyed
std::async
は起動ポリシーなしで動作するので、std::async(square, 5);
コンパイルする。そうすると、システムはスレッドを作成するかどうかを決定します。システムが効率的に実行できるよりも多くのスレッドを実行していない限り、システムはスレッドを作成することを選択しました。あいにく、実装は一般的にそのような状況でスレッドを作成しないことを選択するだけです。そのため、std::launch::async
を使用してその動作をオーバーライドして、システムにスレッドを強制的に作成させる必要があります。競合状態に注意してください。
先物と約束に関する非同期の詳細
スレッドが常に参加していることを保証する
std::thread
デストラクタが呼び出されると、 join()
またはdetach()
いずれかの呼び出しが行われていなければなりません 。スレッドがstd::terminate
されたりデタッチされていない場合、デフォルトではstd::terminate
が呼び出されます。 RAIIを使用すると、これは一般的に次のように簡単に実行できます。
class thread_joiner
{
public:
thread_joiner(std::thread t)
: t_(std::move(t))
{ }
~thread_joiner()
{
if(t_.joinable()) {
t_.join();
}
}
private:
std::thread t_;
}
これは次のように使用されます:
void perform_work()
{
// Perform some work
}
void t()
{
thread_joiner j{std::thread(perform_work)};
// Do some other calculations while thread is running
} // Thread is automatically joined here
これは例外安全性も提供します。スレッドを正常に作成し、他の計算を実行するt()
行われた作業が例外をスローした場合、 join()
は決してスレッドに呼び出されず、プロセスは終了します。
スレッドオブジェクトの再割り当て
私たちは空のスレッドオブジェクトを作成し、後でそれらに作業を割り当てることができます。
スレッドオブジェクトを別のアクティブなjoinable
スレッドに割り当てると、スレッドが置き換えられる前にstd::terminate
が自動的に呼び出されます。
#include <thread>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
}
//create 100 thread objects that do nothing
std::thread executors[100];
// Some code
// I want to create some threads now
for (int i = 0;i < 100;i++)
{
// If this object doesn't have a thread assigned
if (!executors[i].joinable())
executors[i] = std::thread(foo);
}
基本同期
スレッド同期は、他の同期プリミティブの中でも、ミューテックスを使用して達成できます。標準ライブラリにはいくつかのmutex型がありますが、最も単純なのはstd::mutex
です。ミューテックスをロックするには、ロックを作成します。最も単純なロックタイプはstd::lock_guard
です:
std::mutex m;
void worker() {
std::lock_guard<std::mutex> guard(m); // Acquires a lock on the mutex
// Synchronized code here
} // the mutex is automatically released when guard goes out of scope
std::lock_guard
すると、mutexはロックオブジェクトのライフタイム全体にわたってロックされます。リージョンを手動でロックする必要がある場合は、代わりにstd::unique_lock
使用します。
std::mutex m;
void worker() {
// by default, constructing a unique_lock from a mutex will lock the mutex
// by passing the std::defer_lock as a second argument, we
// can construct the guard in an unlocked state instead and
// manually lock later.
std::unique_lock<std::mutex> guard(m, std::defer_lock);
// the mutex is not locked yet!
guard.lock();
// critical section
guard.unlock();
// mutex is again released
}
より多くのスレッド同期構造
条件変数の使用
条件変数は、スレッド間の通信を調整するためにミューテックスと組み合わせて使用されるプリミティブです。これを達成するための排他的または最も効率的な方法ではありませんが、パターンに精通している人にとっては最も簡単な方法です。
1つはstd::unique_lock<std::mutex>
std::condition_variable
を待ちます。これにより、取得を続行するかどうかを決定する前に、コードが共有状態を安全に調べることができます。
以下は、 std::thread
、 std::condition_variable
、 std::mutex
などを使用してプロデューサ/コンシューマのスケッチを作成する方法です。
#include <condition_variable>
#include <cstddef>
#include <iostream>
#include <mutex>
#include <queue>
#include <random>
#include <thread>
int main()
{
std::condition_variable cond;
std::mutex mtx;
std::queue<int> intq;
bool stopped = false;
std::thread producer{[&]()
{
// Prepare a random number generator.
// Our producer will simply push random numbers to intq.
//
std::default_random_engine gen{};
std::uniform_int_distribution<int> dist{};
std::size_t count = 4006;
while(count--)
{
// Always lock before changing
// state guarded by a mutex and
// condition_variable (a.k.a. "condvar").
std::lock_guard<std::mutex> L{mtx};
// Push a random int into the queue
intq.push(dist(gen));
// Tell the consumer it has an int
cond.notify_one();
}
// All done.
// Acquire the lock, set the stopped flag,
// then inform the consumer.
std::lock_guard<std::mutex> L{mtx};
std::cout << "Producer is done!" << std::endl;
stopped = true;
cond.notify_one();
}};
std::thread consumer{[&]()
{
do{
std::unique_lock<std::mutex> L{mtx};
cond.wait(L,[&]()
{
// Acquire the lock only if
// we've stopped or the queue
// isn't empty
return stopped || ! intq.empty();
});
// We own the mutex here; pop the queue
// until it empties out.
while( ! intq.empty())
{
const auto val = intq.front();
intq.pop();
std::cout << "Consumer popped: " << val << std::endl;
}
if(stopped){
// producer has signaled a stop
std::cout << "Consumer is done!" << std::endl;
break;
}
}while(true);
}};
consumer.join();
producer.join();
std::cout << "Example Completed!" << std::endl;
return 0;
}
単純なスレッドプールを作成する
C ++ 11スレッディングプリミティブは比較的低レベルです。それらは、スレッドプールのようなより高いレベルの構造体を書くのに使うことができます:
struct tasks {
// the mutex, condition variable and deque form a single
// thread-safe triggered queue of tasks:
std::mutex m;
std::condition_variable v;
// note that a packaged_task<void> can store a packaged_task<R>:
std::deque<std::packaged_task<void()>> work;
// this holds futures representing the worker threads being done:
std::vector<std::future<void>> finished;
// queue( lambda ) will enqueue the lambda into the tasks for the threads
// to use. A future of the type the lambda returns is given to let you get
// the result out.
template<class F, class R=std::result_of_t<F&()>>
std::future<R> queue(F&& f) {
// wrap the function object into a packaged task, splitting
// execution from the return value:
std::packaged_task<R()> p(std::forward<F>(f));
auto r=p.get_future(); // get the return value before we hand off the task
{
std::unique_lock<std::mutex> l(m);
work.emplace_back(std::move(p)); // store the task<R()> as a task<void()>
}
v.notify_one(); // wake a thread to work on the task
return r; // return the future result of the task
}
// start N threads in the thread pool.
void start(std::size_t N=1){
for (std::size_t i = 0; i < N; ++i)
{
// each thread is a std::async running this->thread_task():
finished.push_back(
std::async(
std::launch::async,
[this]{ thread_task(); }
)
);
}
}
// abort() cancels all non-started tasks, and tells every working thread
// stop running, and waits for them to finish up.
void abort() {
cancel_pending();
finish();
}
// cancel_pending() merely cancels all non-started tasks:
void cancel_pending() {
std::unique_lock<std::mutex> l(m);
work.clear();
}
// finish enques a "stop the thread" message for every thread, then waits for them:
void finish() {
{
std::unique_lock<std::mutex> l(m);
for(auto&&unused:finished){
work.push_back({});
}
}
v.notify_all();
finished.clear();
}
~tasks() {
finish();
}
private:
// the work that a worker thread does:
void thread_task() {
while(true){
// pop a task off the queue:
std::packaged_task<void()> f;
{
// usual thread-safe queue code:
std::unique_lock<std::mutex> l(m);
if (work.empty()){
v.wait(l,[&]{return !work.empty();});
}
f = std::move(work.front());
work.pop_front();
}
// if the task is invalid, it means we are asked to abort:
if (!f.valid()) return;
// otherwise, run the task:
f();
}
}
};
tasks.queue( []{ return "hello world"s; } )
は、 std::future<std::string>
返しますtasks.queue( []{ return "hello world"s; } )
これは、タスクオブジェクトが動作するまでにhello world
ます。
tasks.start(10)
(これは10個のスレッドを開始しますtasks.start(10)
実行してスレッドを作成します。
packaged_task<void()>
の使用は、移動のみの型を格納する型消去されたstd::function
同等物がないためです。カスタムのものを書くことはおそらく、 packaged_task<void()>
を使うよりも速くなるでしょう。
実例 。
C ++ 11では、 result_of_t<blah>
をtypename result_of<blah>::type
に置き換えます。
ミューテックスの詳細。
スレッドローカルストレージ
スレッドローカルストレージは、 thread_local
キーワードを使用して作成できます。 thread_local
指定子で宣言された変数は、 スレッド記憶期間を持つと言われます。
- プログラム内の各スレッドには、それぞれのスレッドローカル変数の独自のコピーがあります。
- 関数(ローカル)スコープのスレッドローカル変数は、最初のコントロールがその定義を通過するときに初期化されます。そのような変数は、
extern
宣言されていない限り、暗黙的に静的です。 - 名前空間またはクラス(非ローカル)スコープを持つスレッドローカル変数は、スレッドの起動の一環として初期化されます。
- スレッドローカル変数は、スレッド終了時に破棄されます。
- クラスのメンバーは静的な場合にのみスレッドローカルにすることができます。そのため、(スレッド、インスタンス)のペアごとに1つのコピーではなく、スレッドごとにその変数のコピーが1つ存在します。
例:
void debug_counter() {
thread_local int count = 0;
Logger::log("This function has been called %d times by this thread", ++count);
}