std::thread
この記事は以下のとおりです.https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md
このセクションでは、
N 3242=11-0012ドラフト第30.3節Threads(p 1133)を参照.
デフォルトコンストラクション関数(1)
thread() noexcept;
初期化コンストラクション関数(2)
template explicit thread(Fn&& fn, Args&&... args);
コピーコンストラクタ[deleted](3)
thread(const thread&) = delete;
Moveコンストラクション関数(4)
thread(thread&& x) noexcept;デフォルトのコンストラクション関数(1)は、空の 構造関数(2)を初期化し、 コピーコンストラクタ(無効化)(3)は、 Moveコンストラクタ(4)、moveコンストラクタ(moveの意味はC++11の新しい概念で、詳細は付録を参照)、呼び出しに成功した後
注意:
std::threadの各種構造関数の例は以下の通りである(参考):
Move割付操作(1)
thread& operator=(thread&& rhs) noexcept;
コピー付与操作[deleted](2)
thread& operator=(const thread&) = delete; Move割付操作(1)現在のオブジェクトができない場合 コピー付与操作(2)が無効になっているため、
次の例を見てください.
その他のメンバー関数
このセクションの例は http://en.cppreference.com
detach関数を呼び出した後: joinable() == false get_id() == std::id()
また、エラーや
実行結果は次のとおりです.
実行結果は次のとおりです. get_id:スレッドIDを取得します. yield:現在のスレッドは実行を放棄し、オペレーティングシステムは別のスレッドの実行をスケジュールします. sleep_until:スレッドは指定された時刻(time point)までスリープし、スレッドが再起動されます. sleep_for:スレッドが指定されたタイムスライス(time span)をスリープし、そのスレッドが再起動されますが、スレッドスケジューリングなどの理由で、実際のスリープ時間は
実行結果は次のとおりです.
参考記事:C++の同時およびマルチスレッド
このセクションでは、
std::thread
を選択します.std::thread
にある
ヘッダファイルに宣言されているため、 std::thread
含める必要がある
ヘッダファイル.
ヘッダファイルの概要
ヘッダファイルはstd::threadスレッドクラスと std::swap
(2つのスレッドオブジェクトを交換する)補助関数.別名空間 std::this_thread
また、
ヘッダファイルにあります.以下はC++11規格で定義されています
ヘッダファイルの概要:N 3242=11-0012ドラフト第30.3節Threads(p 1133)を参照.
namespace std {
#define __STDCPP_THREADS__ __cplusplus
class thread;
void swap(thread& x, thread& y);
namespace this_thread {
thread::id get_id();
void yield();
template
void sleep_until(const chrono::time_point& abs_time);
template
void sleep_for(const chrono::duration& rel_time);
}
}
ヘッダファイルは主に宣言されています std::thread
クラス std::this_thread
ネーミングスペースに宣言されました get_id
, yield
, sleep_until
および sleep_for
などの補助関数について、本章では少し詳しく説明します std::thread
クラスおよび関連関数.std::thread
クラスの概要std::thread
スレッドオブジェクトを表し、C++11標準宣言は次のとおりです.namespace std {
class thread {
public:
// :
class id;
typedef implementation-defined native_handle_type;
// 、 :
thread() noexcept;
template explicit thread(F&& f, Args&&... args);
~thread();
thread(const thread&) = delete;
thread(thread&&) noexcept;
thread& operator=(const thread&) = delete;
thread& operator=(thread&&) noexcept;
// :
void swap(thread&) noexcept;
bool joinable() const noexcept;
void join();
void detach();
id get_id() const noexcept;
native_handle_type native_handle();
// :
static unsigned hardware_concurrency() noexcept;
};
}
std::thread
の中で主に3種類の関数を宣言します:(1).構造関数、コピー構造関数と析出関数;(2).メンバー関数;(3).静的メンバー関数.また、 std::thread::id
スレッドIDを表し、C++11は以下のように宣言する.namespace std {
class thread::id {
public:
id() noexcept;
};
bool operator==(thread::id x, thread::id y) noexcept;
bool operator!=(thread::id x, thread::id y) noexcept;
bool operator(thread::id x, thread::id y) noexcept;
bool operator>=(thread::id x, thread::id y) noexcept;
template
basic_ostream&
operator<< (basic_ostream& out, thread::id id);
// Hash
template struct hash;
template <> struct hash<:id>;
}
std::thread
詳しく説明するstd::thread
構築と割り当てstd::thread
コンストラクタデフォルトコンストラクション関数(1)
thread() noexcept;
初期化コンストラクション関数(2)
template explicit thread(Fn&& fn, Args&&... args);
コピーコンストラクタ[deleted](3)
thread(const thread&) = delete;
Moveコンストラクション関数(4)
thread(thread&& x) noexcept;
std::thread
オブジェクトを実行します.std::thread
オブジェクト、 std::thread
オブジェクト joinable
、新しく生成されたスレッドが呼び出されます. fn
関数、この関数のパラメータは args
与える.std::thread
オブジェクトのコピー不可構造.x
何の意味もない std::thread
オブジェクトを実行します.注意:
joinable
の std::thread
オブジェクトは破棄される前にプライマリスレッドによって破棄される必要があります join
または detached
. std::threadの各種構造関数の例は以下の通りである(参考):
#include
#include
#include
#include
#include
#include
void f1(int n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread " << n << " executing
";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void f2(int& n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 2 executing
";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int main()
{
int n = 0;
std::thread t1; // t1 is not a thread
std::thread t2(f1, n + 1); // pass by value
std::thread t3(f2, std::ref(n)); // pass by reference
std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
t2.join();
t4.join();
std::cout << "Final value of n is " << n << '
';
}
std::thread
割り当てアクションMove割付操作(1)
thread& operator=(thread&& rhs) noexcept;
コピー付与操作[deleted](2)
thread& operator=(const thread&) = delete;
joinable
、右の値の参照を渡す必要があります(rhs
). move
割り当て操作;現在のオブジェクトが joinable
が呼び出されます terminate
()エラーが発生しました.std::thread
オブジェクトはコピーできません.次の例を見てください.
#include
#include
#include // std::chrono::seconds
#include // std::cout
#include // std::thread, std::this_thread::sleep_for
void thread_task(int n) {
std::this_thread::sleep_for(std::chrono::seconds(n));
std::cout << "hello thread "
<< std::this_thread::get_id()
<< " paused " << n << " seconds" << std::endl;
}
int main(int argc, const char *argv[])
{
std::thread threads[5];
std::cout << "Spawning 5 threads...
";
for (int i = 0; i < 5; i++) {
threads[i] = std::thread(thread_task, i + 1);
}
std::cout << "Done spawning threads! Now wait for them to join
";
for (auto& t: threads) {
t.join();
}
std::cout << "All threads joined.
";
return EXIT_SUCCESS;
}
その他のメンバー関数
このセクションの例は http://en.cppreference.com
get_id
:スレッドIDを取得し、タイプを返す std::thread::id
の双曲線コサインを返します.次の例を見てください: #include
#include
#include
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t1(foo);
std::thread::id t1_id = t1.get_id();
std::thread t2(foo);
std::thread::id t2_id = t2.get_id();
std::cout << "t1's id: " << t1_id << '
';
std::cout << "t2's id: " << t2_id << '
';
t1.join();
t2.join();
}
joinable
:スレッドがjoinされるかどうかを確認します.現在のスレッドオブジェクトがアクティブな実行スレッドを表しているかどうかを確認します.デフォルトのコンストラクション関数で作成されたスレッドはjoinできません.また、あるスレッドがタスクを実行したがjoinされていない場合、そのスレッドは依然としてアクティブな実行スレッドとみなされるため、joinされてもよい. #include
#include
#include
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t;
std::cout << "before starting, joinable: " << t.joinable() << '
';
t = std::thread(foo);
std::cout << "after starting, joinable: " << t.joinable() << '
';
t.join();
}
join
:Joinスレッド、この関数を呼び出すと現在のスレッドがブロックされ、 *this
表示されたスレッドはjoinを実行してから返されます. #include
#include
#include
void foo()
{
// simulate expensive operation
std::this_thread::sleep_for(std::chrono::seconds(1));
}
void bar()
{
// simulate expensive operation
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::cout << "starting first helper...
";
std::thread helper1(foo);
std::cout << "starting second helper...
";
std::thread helper2(bar);
std::cout << "waiting for helpers to finish..." << std::endl;
helper1.join();
helper2.join();
std::cout << "done!
";
}
detach
:Detachスレッド.現在のスレッドオブジェクトが表す実行インスタンスをスレッドオブジェクトから分離し、スレッドの実行を個別に行うことができます.スレッドの実行が完了すると、割り当てられたリソースが解放されます.detach関数を呼び出した後:
*this
スレッド実行インスタンスを表すものではありません.また、エラーや
joinable() == false
は、 std::system_error
. #include
#include
#include
void independentThread()
{
std::cout << "Starting concurrent thread.
";
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Exiting concurrent thread.
";
}
void threadCaller()
{
std::cout << "Starting thread caller.
";
std::thread t(independentThread);
t.detach();
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Exiting thread caller.
";
}
int main()
{
threadCaller();
std::this_thread::sleep_for(std::chrono::seconds(5));
}
swap
:Swapスレッドは、2つのスレッドオブジェクトが表す最下位ハンドル(underlying handles)を交換します. #include
#include
#include
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
void bar()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t1(foo);
std::thread t2(bar);
std::cout << "thread 1 id: " << t1.get_id() << std::endl;
std::cout << "thread 2 id: " << t2.get_id() << std::endl;
std::swap(t1, t2);
std::cout << "after std::swap(t1, t2):" << std::endl;
std::cout << "thread 1 id: " << t1.get_id() << std::endl;
std::cout << "thread 2 id: " << t2.get_id() << std::endl;
t1.swap(t2);
std::cout << "after t1.swap(t2):" << std::endl;
std::cout << "thread 1 id: " << t1.get_id() << std::endl;
std::cout << "thread 2 id: " << t2.get_id() << std::endl;
t1.join();
t2.join();
}
実行結果は次のとおりです.
thread 1 id: 1892
thread 2 id: 2584
after std::swap(t1, t2):
thread 1 id: 2584
thread 2 id: 1892
after t1.swap(t2):
thread 1 id: 1892
thread 2 id: 2584
native_handle
:native handle( std::thread
の実装はオペレーティングシステムに関連しているため、この関数は std::thread
具体的には、関連するスレッドハンドルが実装され、例えば、Unix/LinuxのようなPosix規格に適合するプラットフォームの下でPthreadライブラリである. #include
#include
#include
#include
#include
std::mutex iomutex;
void f(int num)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
sched_param sch;
int policy;
pthread_getschedparam(pthread_self(), &policy, &sch);
std::lock_guard<:mutex> lk(iomutex);
std::cout << "Thread " << num << " is executing at priority "
<< sch.sched_priority << '
';
}
int main()
{
std::thread t1(f, 1), t2(f, 2);
sched_param sch;
int policy;
pthread_getschedparam(t1.native_handle(), &policy, &sch);
sch.sched_priority = 20;
if(pthread_setschedparam(t1.native_handle(), SCHED_FIFO, &sch)) {
std::cout << "Failed to setschedparam: " << std::strerror(errno) << '
';
}
t1.join();
t2.join();
}
実行結果は次のとおりです.
Thread 2 is executing at priority 0
Thread 1 is executing at priority 20
hardware_concurrency
[static]:ハードウェアの同時特性を検出し、現在のプラットフォームのスレッド実装でサポートされているスレッドの同時数を返しますが、戻り値はシステムヒント(hint)としてのみ使用されます. #include
#include
int main() {
unsigned int n = std::thread::hardware_concurrency();
std::cout << n << " concurrent threads are supported.
";
}
std::this_thread
ネーミングスペースの関連補助関数の説明 #include
#include
#include
#include
std::mutex g_display_mutex;
void foo()
{
std::thread::id this_id = std::this_thread::get_id();
g_display_mutex.lock();
std::cout << "thread " << this_id << " sleeping...
";
g_display_mutex.unlock();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t1(foo);
std::thread t2(foo);
t1.join();
t2.join();
}
#include
#include
#include
// "busy sleep" while suggesting that other threads run
// for a small amount of time
void little_sleep(std::chrono::microseconds us)
{
auto start = std::chrono::high_resolution_clock::now();
auto end = start + us;
do {
std::this_thread::yield();
} while (std::chrono::high_resolution_clock::now() < end);
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
little_sleep(std::chrono::microseconds(100));
auto elapsed = std::chrono::high_resolution_clock::now() - start;
std::cout << "waited for "
<< std::chrono::duration_cast<:chrono::microseconds>(elapsed).count()
<< " microseconds
";
}
template< class Clock, class Duration >
void sleep_until( const std::chrono::time_point& sleep_time );
sleep_duration
表示されるタイムスライスはもっと長いです. template< class Rep, class Period >
void sleep_for( const std::chrono::duration& sleep_duration );
#include
#include
#include
int main()
{
std::cout << "Hello waiter" << std::endl;
std::chrono::milliseconds dura( 2000 );
std::this_thread::sleep_for( dura );
std::cout << "Waited 2000 ms
";
}
実行結果は次のとおりです.
Hello waiter
Waited 2000 ms
参考記事:C++の同時およびマルチスレッド