Lang/C++/C++11/thread
の編集
[
トップ
] [
編集
|
差分
|
履歴
|
添付
|
リロード
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
]
-- 雛形とするページ --
App
App/archiver
App/audio
App/audio/audacity
App/audio/mpg321
App/audio/puddletag
App/base64
App/Browser
App/column
App/diff-patch
App/downloader
App/ffmpeg
App/ffprobe
App/find
App/gimp
App/gnuplot
App/ImageMagick
App/info
App/inxi
App/LibreOffice
App/locate
App/md5
App/Music
App/Music/frescobaldi
App/Music/MuseScore
App/OCR
App/od
App/OpenOffice.org
App/pagers
App/pdf
App/QRコード
App/ripgrep
App/screen
App/script
App/sed
App/selenium
App/signal-desktop
App/skype
App/sylpheed
App/tmux
App/ttyrec
App/uniq
App/UUID
App/wget
App/xargs
App/テキスト処理
App/仮想化
App/仮想化/Docker
App/仮想化/qemu
App/仮想化/webos
AutoTicketLinkName
BracketName
CentOS
CentOS/yum
CrossCompile
Debian
Debian/kernel
Debian/kernel/ver8-or-older
Debian/Plan9関連
Debian/ver5
Debian/ver6
Debian/ver7
Debian/ver8
Debian/ver9
Debian/ver10
Debian/ver11
Debian/ver12
Debian/X
Debian/X/日本語
Debian/パッケージ管理
Deveice/Sensor/MotionSensor
Device
Device/Mouse
Device/Net/wifi
Device/Scanner
Device/Sensor
Device/Sensor/MotionSensor
Device/tty
Device/xmodmap
Editor
Editor/atom
Editor/binary
Editor/binary/beav
Editor/binary/hexedit
Editor/emacs
Editor/emacs/clang-format
Editor/emacs/flycheck
Editor/emacs/gdb-mode
Editor/emacs/github/copilot
Editor/emacs/html-mode
Editor/emacs/lisp
Editor/emacs/lsp-mode
Editor/emacs/markdown-mode
Editor/emacs/neotree
Editor/emacs/package-install
Editor/emacs/sdic
Editor/emacs/speedbar
Editor/emacs/XEmacs
Editor/emacs/ローカルビルド
Editor/emacs/多言語
Editor/fte
Editor/vi
FormattingRules
FrontPage
Help
InterWiki
InterWikiName
InterWikiSandBox
iPhone
Lang
Lang/C++
Lang/C++/Boost
Lang/C++/C++11
Lang/C++/C++11/thread
Lang/C++/C++11/コンテナ
Lang/C++/C++11/コンテナ/string
Lang/C++/C++11/初期化子
Lang/C++/C++17
Lang/C++/C++20
Lang/C++/error
Lang/C++/macro
Lang/C++/template
Lang/C++/コンテナ
Lang/C++/処理系
Lang/C-Sharp
Lang/Go
Lang/html
Lang/html/5
Lang/html/5/canvas
Lang/html/css
Lang/Java
Lang/Java/jar
Lang/Objective-C
Lang/Pascal
Lang/Perl
Lang/Perl/CGI
Lang/Perl/thread
Lang/Perl/utf-8
Lang/pike
Lang/Python
Lang/Python/Class
Lang/Python/Error
Lang/Python/import対象パス
Lang/Python/pip
Lang/Python/基本文法
Lang/Python/開発環境
Lang/Rust
Lang/sed
Lang/shell
Lang/shell/bash
Lang/shell/bash/設定
Lang/shell/bash/設定/command-not-found
Lang/Tcl_Tk_Expect
Lang/Tcl_Tk_Expect/Ctrl-キーマップ
Lang/TypeScript
Lang/xml
Mac
Memo
memo
MenuBar
midi
Multimedia
Net
Net/apache
Net/apache2
Net/dig
Net/dns/server
Net/domain
Net/http/アクセス制御
Net/iptables
Net/IRC
Net/Jenkins
Net/lighttpd
Net/mail
Net/mail/X-Face
Net/news
Net/news/gnus
Net/ntp
Net/pukiwiki
Net/pukiwiki/plugin
Net/resolv.conf
Net/SPAM
Net/ss
Net/tcpdump
Net/traceroute
Net/twitter
Net/vpn
Net/web
Net/webAPI
Net/wifi
Net/wifi/AP
Net/監視
otherlinks
PHP
Plan9
PPC
PPC/LinuxPPC
PPC/MkLinux
Prog
Prog/AI
Prog/autotools
Prog/cmake
Prog/Compiler/gcc
Prog/CrossReferencer
Prog/debug
Prog/debug/gdb
Prog/debug/lldb
Prog/debug/ltrace
Prog/debug/memory
Prog/debug/strace
Prog/formatter/clang-format
Prog/GoogleTest
Prog/Gtk
Prog/IDE
Prog/IDE/Geany
Prog/ldd
Prog/make
Prog/nm
Prog/Profiler
Prog/Qt
Prog/readelf
Prog/Sound
Prog/StaticCodeAnalysis
Prog/StaticCodeAnalysis/cppcheck
Prog/strip
Prog/TagSystem
Prog/TagSystem/cscope
Prog/TagSystem/ctags-exuberant
Prog/TagSystem/ebrowse
Prog/TagSystem/etags
Prog/TagSystem/gtags
Prog/TagSystem/id-utils
Prog/TagSystem/lctags
Prog/TagSystem/rtags
Prog/TagSystem/SilentBob
Prog/X/X11
Prog/プロセス間通信
Prog/プロセス間通信/SharedMemory
Prog/プロセス間通信/singal
Prog/人為的なシグナル
Prog/分散ビルド/distcc
Prog/逆アセンブル
PukiWiki
PukiWiki/1.4
PukiWiki/1.4/Manual
PukiWiki/1.4/Manual/Plugin
PukiWiki/1.4/Manual/Plugin/A-D
PukiWiki/1.4/Manual/Plugin/E-G
PukiWiki/1.4/Manual/Plugin/H-K
PukiWiki/1.4/Manual/Plugin/L-N
PukiWiki/1.4/Manual/Plugin/O-R
PukiWiki/1.4/Manual/Plugin/S-U
PukiWiki/1.4/Manual/Plugin/V-Z
RecentDeleted
RS232C
SandBox
Security
Security/aide
Security/chkrootkit
Security/clamav
Security/gpg
Security/pkcs
Security/ssh
Security/test
Security/パスワード解析
System
System/Audio
System/beep
System/bluetooth
System/cpu
System/dm-crypt
System/fs
System/fs/ext2-3
System/fs/mount
System/fs/nfs
System/fs/tmpfs
System/fs/xfs
System/fs/大容量のファイルを高速に作成する
System/fs/強制物理フォーマット
System/grub
System/Hardware情報
System/HDD/IDE/チューニング
System/input
System/iostat
System/Kernel
System/Kernel/Module
System/locale
System/memory
System/OpenCL
System/os
System/power/battery
System/Process
System/random
System/rpm
System/runlevel
System/signal
System/sudo
System/swap
System/USB-Boot
System/user
System/シリアル接続
System/環境変数
TeX
TeX/MusiXTeX
tools
Top/Editor/emacs/python開発環境
transcription/old
Ubuntu
VersionCtl
VersionCtl/git
VersionCtl/git/Gerrit
VersionCtl/git/Gerrit/old
VersionCtl/git/git-gutter+
VersionCtl/git/git-imerge
VersionCtl/git/magit
VersionCtl/git/magit/2.1.0
VersionCtl/git/magit/2.8.0
VersionCtl/git/magit/2.13.1
VersionCtl/git/magit/v3.2.1
VersionCtl/git/magit/v3.3.0
VersionCtl/git/subversion からの移行
VersionCtl/git/subversion とのコマンド対比
VersionCtl/git/tig
VersionCtl/git/統計
VersionCtl/hg
VersionCtl/Mercurial
VersionCtl/repo
VersionCtl/Subversion
WikiEngines
WikiName
WikiWikiWeb
Windows
Windows/cmd
X
X/DisplayManager
X/DisplayManager/gdm3
X/DisplayManager/lightdm
X/font
X/font/old
X/GNOME3
X/input
X/input/OnScreenKeyboard
X/input/キーアサインを変更する
X/input/グラゴル文字
X/input/日本語
X/input/日本語/ATOK X3
X/input/日本語/ibus
X/input/日本語/uim
X/KDE
X/remote
X/screensaver
X/terminal
X/terminal/mlterm
X/terminal/urxvt
X/terminal/uxterm
X/vnc
X/vnc/tigervnc
X/WindowManager
X/WindowManager/9wm
X/WindowManager/amiwm
X/WindowManager/awesome
X/WindowManager/CDE
X/WindowManager/cinnamon
X/WindowManager/dwm
X/WindowManager/flwm
X/WindowManager/i3
X/WindowManager/jwm
X/WindowManager/matchbox
X/WindowManager/MATE
X/WindowManager/mlvwm
X/WindowManager/qvwm
X/WindowManager/ratpoison
X/WindowManager/ude
X/WindowManager/WindowMaker
X/WindowManager/xfce4
X/xdotool
X/Xnest
X/xrdp
X/xvfb
YukiWiki
スタジオ・ホール
旅
旅/Czech
旅/三重
旅/京都・滋賀
旅/兵庫
旅/千葉
旅/各務
旅/和歌山
旅/埼玉
旅/山形
旅/岐阜
旅/岡山
旅/岩手
旅/島根
旅/愛媛
旅/新潟
旅/東京
旅/栃木
旅/神奈川
旅/秋田
旅/群馬
旅/茨城
旅/長野・山梨
旅/静岡
旅/静岡/伊豆
未整理
食
食/チェコ
食/各国
食/料理
食/激辛
食/蕎麦
食/郷土料理
#topicpath ////////////////////////////////////////////////////////////////////////////// * 目次 [#z44d9e2e] #contents(); ////////////////////////////////////////////////////////////////////////////// * 初めに [#s8cb5470] - C++11 では thread がサポートされている。→ std::thread class を使う - gcc では build 時に -pthread オプションを要求される場合がある(ビルドが通っても実行できなかった場合には、これを忘れていないか、まず疑ってみる) ////////////////////////////////////////////////////////////////////////////// * std::thread [#std-thread] - C++11 では、 std::thread class を用いてスレッドの生成と管理を行う。 - std::thread は、 ''copy 不可・move 不可'' となっている。 ////////////////////////////////////////////////////////////////////////////// * thread の作成 [#create-thread] - 関数 foo(), bar() をそれぞれ別の thread で実行する // 排他出力処理 mutex print_mutex; void print( const string &s ) { lock_guard<mutex> lk(print_mutex); cout << s << endl; } void foo() { print( "foo" ); } void bar() { print( "bar" ); } // foo(), bar() をそれぞれ別の thread で実行 thread th1{ foo }; thread th2{ bar }; th1.join(); th2.join(); //============================================================================ ** 引数付き関数を別 thread で実行する [#bc57c0ea] - 書式 std::thread object_name{ <thread-func>, <thread-func-arg> }; - 関数や関数オブジェクトの呼び出し演算子が仮引数を取る場合、 std::thread のコンストラクタの第2引数以降に関数や関数オブジェクトの関数呼び出し演算子へ与える実引数を渡す。 - std::thread のコンストラクタに渡された実引数は、受け取る関数が参照渡しとして宣言されていたとしても、コピーされて std::thread クラスのオブジェクト内に保存される。 -- 上記コピーを避けたい場合は、実引数をムーブするか、 std::ref() 関数でくるんで渡す。 -- std::ref() を使用した場合、作成した thread に参照が渡されるので、元のオブジェクトの寿命に注意すること。 - thread 内で例外が発生し、 catch されないまま thread 外に送出されると、std::terminate() が呼び出され、プログラムが強制終了する。 - 例 void foo(int id) { print( "foo: %d", id ); } int foo_arg = 20; std::thread th1{ foo, foo_arg }; //============================================================================ ** クラスメンバ関数をスレッド関数にする場合 [#vf4bce69] - class member な関数は、そのままではスレッド関数に指定出来ない。スレッド生成時に &color(purple){this}; を渡してやることで生成可能になる。 class ClsA { public: ClsA() {} virtual ~ClsA() {} void Init(); ... void ThreadMain(); private: std::thread::id m_thread_id_main; ... }; ~ // スレッドの開始 void ClsA::Init() { // スレッド生成時に this を渡してやる std::thread main_thread( &ClsA::ThreadMain, this ); m_thread_id_main = main_thread.get_id(); // detach する。 main_thread.detach(); .... } ////////////////////////////////////////////////////////////////////////////// * thread 終了を待機する [#join] - thread の終了を待機するところで std::thread::join() を呼ぶ。 std::thread th1( foo() ); th1.join(); cout << "th1 is joined" << endl; - join() 呼び出しが完了するまで、 join() の実行元スレッドはブロックされる。 - join() の呼び出しが完了すると、そのオブジェクト(上記例では th1)はどのスレッドも管理していない状態になる。 - 既に join() の呼び出しが完了しているオブジェクトに対して join() を呼び出すと、 std::errc::invalid_argument をエラーコードに設定した std::system_error 例外が送出される。 - std::thread::joinable() の返り値を見ることによって、そのオブジェクトの join() を問題なく呼び出せるかどうかを確認することが出来る。 - join() 呼び出し可能なオブジェクトが破棄される場合、 std::thread::detach() や std::thread::join() が自動的に呼び出されず、 std::terminate() が呼び出されてプログラムが即時に終了することになっている。 - join() 可能なオブジェクトは、破棄される前に必ず join() するか detach() を呼び出すようにしなければならない。 ////////////////////////////////////////////////////////////////////////////// * thread 終了を待機可能かどうか判定する [#vd818d20] - std::thread::joinable() はスレッドの終了を待機可能かどうかを返す。 bool std::thread::joinable() const; ////////////////////////////////////////////////////////////////////////////// * thread を手放す [#t3ea7895] - void srd::thread::detach() を実行する using namespace std; void foo() { // 時間の掛かる処理 ... } void bar() { thread th1( foo() ); th1.detach(); // スレッドを手放す } // 関数 bar() が終了しても、スレッド foo() は別スレッドにて実行中 - std::thread::detach() が呼び出されると、そのオブジェクト(上記例の th1)はどのスレッドも管理していない状態になる。 - 既に detach() が呼び出されたオブジェクトに対して detach() を呼び出すと、 std::errc::invalid_argument をエラーコードに設定された std::system_error 例外が送出される。 - std::exit() が呼ばれると、実行中のスレッドがあってもプログラムは終了する。この時、実行中のスレッドから、プログラム終了に伴い破棄されたオブジェクトにアクセスすると、未定義の動作を引き起こす。 ////////////////////////////////////////////////////////////////////////////// * thread の識別 [#x6a39824] - std::thread::id std::thread::get_id() を呼ぶ void foo( ostringstream &os ) { os << "foo(): " << this_thread::get_id() << endl; } void bar( ostringstream &os ) { os << "bar(): " << this_thread::get_id() << endl; } ostringstream os_foo; ostringstream os_bar; thread th1( foo(), ref( os_foo ) ); thread th2( bar(), ref( os_bar ) ); // それぞれの thread ID を得る thread::id id1 = th1.get_id(); thread::id id2 = th2.get_id(); thread::id id3 = this_thread::get_id(); assert( id1 != is2 ); assert( id1 != is3 ); assert( id2 != is3 ); th1.join(); th2.join(); cout << os_foo.str(); cout << os_bar.str(); cout << "main thread : " << this_thread::get_id() << endl; - std::thread::id は文字列型なので、無効な値(実行中でないスレッドオブジェクトの get_id() の返り値)をプリントすると、下記のような文字列が出力される: thread::id of a non-executing thread - std::thread::id は、std::thread が管理する一意な ID - 実行が終了したスレッドの std::thread::id は、再利用される可能性がある。そのため、スレッドの寿命を越えて std::thread::id を使用してはならない。 ////////////////////////////////////////////////////////////////////////////// * 現在の thread の処理を明け渡す [#s20f6dff] void std::this_thread::yield() noexcept; - この関数は、プロセッサを専有し続けることなく、適宜他のスレッドに処理を譲る。 - OS が CPU を管理しない(=ノンエンプリエンティブな)環境では、1つのスレッドが長時間にわたって動作し続ける可能性がある。この関数を呼び出すと、現在実行しているスレッドに割り当てられたプロセッサの制御を、他のスレッドに自動で明け渡すことが出来る。 ////////////////////////////////////////////////////////////////////////////// * 現在の thread をスリープする [#q1284bbf] namespace std { namespace this_thread { template <class Clock, class Duration> void sleep_until( const chrono::time_point<Clock, Duration>& abs_time); template <class Rep, class Period> void sleep_for(const chrono::duration<Rep, Period>& rel_time); } } - この関数を使用することで、現在実行しているスレッドの処理を中断し、実行をブロックすることが出来る。 -- sleep_until() 実引数で渡した自国まで処理を中断する。 -- sleep_for() 実引数で渡した時間だけ処理を中断する。 ////////////////////////////////////////////////////////////////////////////// * 並行実行できる thread の数を取得する [#te2d6f95] namespace std { class thread { public: static unsigned int hardware_concurrency() noexcept; }; } - この関数は、現在実行している環境で並行実行出来るスレッド数を返す。 - この関数によって返される値はあくまで参考値であり、実際にどの程度並行実行出来るかは、実装や環境に依存する。 - 並行実行出来るスレッド数を計算できない場合や、実装によって正しく定義されていない場合、この関数は 0 を返す。 ////////////////////////////////////////////////////////////////////////////// * mutex [#mutex] namespace std { class mutex { public: void lock(); bool try_lock(); void unlock(); }; class recursive_mutex { public: void lock(); bool try_lock(); void unlock(); }; } - lock() -- オブジェクトに対しロックを掛ける -- 呼び出した時点で、同じオブジェクトに対し既にロックがかけられている場合、そのロックが解除されるまでブロックされる - try_lock(): -- ロックを試行する |~状態 |~try_loclk() 呼び出し結果 |~備考 | |どのスレッドもそのオブジェクトにロックをかけていない時 |ロックをかけ、true を返す | | |他のスレッドから既にロックがかけられている場合 |ロックに失敗し、false が返る | | - unlock() -- オブジェクトに掛けたロックを解除する //============================================================================ ** std::mutex [#std-mutex] - 再帰的にロックは掛けられない →1スレッド内で同じオブジェクトに2回ロックを掛けるとデッドロックが発生する。 - デッドロックにはまった場合、実装系がデッドロックを発見出来る場合には、 std::errc::resource_deadlock_would_occur を設定した std::system_error 例外が送出される可能性がある。 //============================================================================ ** std::recursive_mutex [#recursive_mutex] - 再帰的にロック出来る mutex - スレッド間で排他的だが、同一スレッド内からは同一オブジェクトに対し複数回のロックを掛けることが出来る。 -- ロックしたのと同じ回数アンロックすると、ロックが解除される。 - 何回までロックを掛けられるかは規定がない。上限を超えた場合の挙動は -- try_lock(): false が返る -- lock() : std::system_error 例外を送出 //============================================================================ ** std::timed_mutex [#timed_mutex] - タイムアウト付き mutex - lock() / try_lock() も使用出来る - タイムアウト指定出来るロック関数が用意されている -- try_lock_for() : 指定時間だけロックを試行する -- try_lock_until() : 指定時刻までロックを試行する -- タイムアウト付きロック関数は、タイムアウトまでにロック出来た場合には true, 出来なかった場合は false を返す。 //============================================================================ ** std::timed_recursive_mutex [#timed_recursive_mutex] - 再帰的にロックを掛けられる、タイムアウト付き mutex - std::recursivemutex, std::timed_mutex の両方の機能を備えている。 //============================================================================ ** std::shared_mutex [C++17] [#shared_timed_mutex] //============================================================================ ** std::shared_timed_mutex [C++14] [#shared_timed_mutex] ////////////////////////////////////////////////////////////////////////////// * リソースのロックを管理する [#o619d861] //============================================================================ ** std::lock_guard [#lock_guard] - ロックのインスタンスを生成し、それが有効な間をロックの対象とする - よって、明示的なロック解除の I/F は使わない #include <mutex> { std::mutex mtx; { std::lock_guard<mutex> lk( mtx ); // ここからロックが掛かる // 処理... } // lk の有効なスコープを抜け、lk のインスタンスが消滅すると同時にロック解除となる } //============================================================================ ** std::unique_lock [#unique_lock] - コンストラクタ第2引数に std::defer_lock 変数を渡すと、ロックのタイミングを遅延する - 後からロックを取得するには、 lock() / try_lock() を呼び出す - 使用する mutex が try_lock_for(), try_lock_until() メンバ関数を持っている場合、コンストラクタ第2仮引数に std::chrono::duration / std::chrono::time_point クラスのオブジェクトを渡し、タイムアウト付きでロック取得を試行出来る。 - コンストラクタに std::defer_lock が渡され、ロックを取得するタイミングが遅延されている場合、 try_loc_for() / try_lock_until() メンバを呼び出し、タイムアウト付きでロック取得を試行出来る。 - release() : 管理しているオブジェクトを手放す - unlock() : 任意のタイミングでロックを解除する - owns_lock() : 現在ロックを取得しているかどうかを確認出来る ////////////////////////////////////////////////////////////////////////////// * 複数のリソースをロックする [#w31f69a0] ////////////////////////////////////////////////////////////////////////////// * ロックせずに排他アクセスする [#j61ebc0e] //============================================================================ ** std::memory_order [#e66f6277] //============================================================================ ** std::atomic [#qf70be40] ////////////////////////////////////////////////////////////////////////////// * スレッドセーフに1度だけ関数を呼び出す [#g8f42eac] ////////////////////////////////////////////////////////////////////////////// * 条件変数を使用する [#kb3e1964] ////////////////////////////////////////////////////////////////////////////// * thread をまたいで値や例外を受け渡す [#std-promise-future] - スレッド間で非同期に値や例外を受け渡す方法として、 std::promise / std::future が提供されている //============================================================================ ** 使い方 [#std-promise-future-usage] + std::promise の実体生成し下記を呼び、その実体に対応する std::future の実体を取得する。 get_future() + 値と例外は、以下のいずれかを用いてどちらか一つを、''1回だけ''設定出来る。 set_value() set_exception() -- 値または例外が既に設定された状態で再度これらが呼び出された場合、 std::future_error 例外が送出される。 -- 対応する std::future が値や例外を受け取るタイミングを、このスレッドの終了時まで送らせたい場合は、以下のいずれかを使う set_value_at_thread_exit() set_exception_at_thread_exit() + std::promise の get_future() で取得した std::future の get() 関数を使い、std::promise で設定された値を受け取る。 -- 設定されたのが値ではなく例外だった場合は、例外が送出される。 -- まだ std::promise で値も例外も設定されていなかった場合、内部で wait() を呼び出す。 --- wait() は、値または例外の設定を待機するもので、設定されるまでは呼び出し元のスレッドをブロックする。 -- 値の設定待ちに条件を付けたい場合は、以下を使用する wait_for( std::chrono::duration d ) // 指定した時間 d だけ待機する wait_until( std::chrono::time_point tp ) // 指定した時刻 tp まで待機する これらの返り値は以下 std:future_status::ready // 指定時間内に値か例外が設定された std:future_status::timeout // 値も例外も設定されないまま指定時間を過ぎた std:future_status::deferred // 実行のタイミングが延期されている - ''std::future の get() を呼び出すと、 std::promise との対応関係は解除される。'' ''その状態で get() を呼び出した場合の動作は未定義である。'' //============================================================================ ** 使い方の例 [#std-promise-future-example] - 以下は、 std::string をやり取りする例: #include <iostream> #include <thread> #include<future> static std::mutex g_print_mutex; void LockPrintf( const std::string & str ) { std::lock_guard<std::mutex> lk( g_print_mutex ); std::cout << str << std::endl; } void ThreadFuncA( std::promise<std::string> pr ) { std::string sendstr ="from ThreadFuncA"; pr.set_value( sendstr ); // 他のスレッドに受け渡す値を設定 LockPrintf( "ThreadFuncA" ); } int main( int argc, char * argv[] ) { std::promise<std::string> prom; std::future<std::string> futu = prom.get_future(); // promise で他スレッドで設定された値を取得 std::thread th_a( ThreadFuncA, move( prom ) ); std::string future_string = "Rcv from A: " + futu.get(); LockPrintf( future_string ); th_a.join(); return( 0 ); } ////////////////////////////////////////////////////////////////////////////// * 非同期処理をする [#kd28f4df] ////////////////////////////////////////////////////////////////////////////// * スレッドローカル変数を使用する [#k24f952f]
#topicpath ////////////////////////////////////////////////////////////////////////////// * 目次 [#z44d9e2e] #contents(); ////////////////////////////////////////////////////////////////////////////// * 初めに [#s8cb5470] - C++11 では thread がサポートされている。→ std::thread class を使う - gcc では build 時に -pthread オプションを要求される場合がある(ビルドが通っても実行できなかった場合には、これを忘れていないか、まず疑ってみる) ////////////////////////////////////////////////////////////////////////////// * std::thread [#std-thread] - C++11 では、 std::thread class を用いてスレッドの生成と管理を行う。 - std::thread は、 ''copy 不可・move 不可'' となっている。 ////////////////////////////////////////////////////////////////////////////// * thread の作成 [#create-thread] - 関数 foo(), bar() をそれぞれ別の thread で実行する // 排他出力処理 mutex print_mutex; void print( const string &s ) { lock_guard<mutex> lk(print_mutex); cout << s << endl; } void foo() { print( "foo" ); } void bar() { print( "bar" ); } // foo(), bar() をそれぞれ別の thread で実行 thread th1{ foo }; thread th2{ bar }; th1.join(); th2.join(); //============================================================================ ** 引数付き関数を別 thread で実行する [#bc57c0ea] - 書式 std::thread object_name{ <thread-func>, <thread-func-arg> }; - 関数や関数オブジェクトの呼び出し演算子が仮引数を取る場合、 std::thread のコンストラクタの第2引数以降に関数や関数オブジェクトの関数呼び出し演算子へ与える実引数を渡す。 - std::thread のコンストラクタに渡された実引数は、受け取る関数が参照渡しとして宣言されていたとしても、コピーされて std::thread クラスのオブジェクト内に保存される。 -- 上記コピーを避けたい場合は、実引数をムーブするか、 std::ref() 関数でくるんで渡す。 -- std::ref() を使用した場合、作成した thread に参照が渡されるので、元のオブジェクトの寿命に注意すること。 - thread 内で例外が発生し、 catch されないまま thread 外に送出されると、std::terminate() が呼び出され、プログラムが強制終了する。 - 例 void foo(int id) { print( "foo: %d", id ); } int foo_arg = 20; std::thread th1{ foo, foo_arg }; //============================================================================ ** クラスメンバ関数をスレッド関数にする場合 [#vf4bce69] - class member な関数は、そのままではスレッド関数に指定出来ない。スレッド生成時に &color(purple){this}; を渡してやることで生成可能になる。 class ClsA { public: ClsA() {} virtual ~ClsA() {} void Init(); ... void ThreadMain(); private: std::thread::id m_thread_id_main; ... }; ~ // スレッドの開始 void ClsA::Init() { // スレッド生成時に this を渡してやる std::thread main_thread( &ClsA::ThreadMain, this ); m_thread_id_main = main_thread.get_id(); // detach する。 main_thread.detach(); .... } ////////////////////////////////////////////////////////////////////////////// * thread 終了を待機する [#join] - thread の終了を待機するところで std::thread::join() を呼ぶ。 std::thread th1( foo() ); th1.join(); cout << "th1 is joined" << endl; - join() 呼び出しが完了するまで、 join() の実行元スレッドはブロックされる。 - join() の呼び出しが完了すると、そのオブジェクト(上記例では th1)はどのスレッドも管理していない状態になる。 - 既に join() の呼び出しが完了しているオブジェクトに対して join() を呼び出すと、 std::errc::invalid_argument をエラーコードに設定した std::system_error 例外が送出される。 - std::thread::joinable() の返り値を見ることによって、そのオブジェクトの join() を問題なく呼び出せるかどうかを確認することが出来る。 - join() 呼び出し可能なオブジェクトが破棄される場合、 std::thread::detach() や std::thread::join() が自動的に呼び出されず、 std::terminate() が呼び出されてプログラムが即時に終了することになっている。 - join() 可能なオブジェクトは、破棄される前に必ず join() するか detach() を呼び出すようにしなければならない。 ////////////////////////////////////////////////////////////////////////////// * thread 終了を待機可能かどうか判定する [#vd818d20] - std::thread::joinable() はスレッドの終了を待機可能かどうかを返す。 bool std::thread::joinable() const; ////////////////////////////////////////////////////////////////////////////// * thread を手放す [#t3ea7895] - void srd::thread::detach() を実行する using namespace std; void foo() { // 時間の掛かる処理 ... } void bar() { thread th1( foo() ); th1.detach(); // スレッドを手放す } // 関数 bar() が終了しても、スレッド foo() は別スレッドにて実行中 - std::thread::detach() が呼び出されると、そのオブジェクト(上記例の th1)はどのスレッドも管理していない状態になる。 - 既に detach() が呼び出されたオブジェクトに対して detach() を呼び出すと、 std::errc::invalid_argument をエラーコードに設定された std::system_error 例外が送出される。 - std::exit() が呼ばれると、実行中のスレッドがあってもプログラムは終了する。この時、実行中のスレッドから、プログラム終了に伴い破棄されたオブジェクトにアクセスすると、未定義の動作を引き起こす。 ////////////////////////////////////////////////////////////////////////////// * thread の識別 [#x6a39824] - std::thread::id std::thread::get_id() を呼ぶ void foo( ostringstream &os ) { os << "foo(): " << this_thread::get_id() << endl; } void bar( ostringstream &os ) { os << "bar(): " << this_thread::get_id() << endl; } ostringstream os_foo; ostringstream os_bar; thread th1( foo(), ref( os_foo ) ); thread th2( bar(), ref( os_bar ) ); // それぞれの thread ID を得る thread::id id1 = th1.get_id(); thread::id id2 = th2.get_id(); thread::id id3 = this_thread::get_id(); assert( id1 != is2 ); assert( id1 != is3 ); assert( id2 != is3 ); th1.join(); th2.join(); cout << os_foo.str(); cout << os_bar.str(); cout << "main thread : " << this_thread::get_id() << endl; - std::thread::id は文字列型なので、無効な値(実行中でないスレッドオブジェクトの get_id() の返り値)をプリントすると、下記のような文字列が出力される: thread::id of a non-executing thread - std::thread::id は、std::thread が管理する一意な ID - 実行が終了したスレッドの std::thread::id は、再利用される可能性がある。そのため、スレッドの寿命を越えて std::thread::id を使用してはならない。 ////////////////////////////////////////////////////////////////////////////// * 現在の thread の処理を明け渡す [#s20f6dff] void std::this_thread::yield() noexcept; - この関数は、プロセッサを専有し続けることなく、適宜他のスレッドに処理を譲る。 - OS が CPU を管理しない(=ノンエンプリエンティブな)環境では、1つのスレッドが長時間にわたって動作し続ける可能性がある。この関数を呼び出すと、現在実行しているスレッドに割り当てられたプロセッサの制御を、他のスレッドに自動で明け渡すことが出来る。 ////////////////////////////////////////////////////////////////////////////// * 現在の thread をスリープする [#q1284bbf] namespace std { namespace this_thread { template <class Clock, class Duration> void sleep_until( const chrono::time_point<Clock, Duration>& abs_time); template <class Rep, class Period> void sleep_for(const chrono::duration<Rep, Period>& rel_time); } } - この関数を使用することで、現在実行しているスレッドの処理を中断し、実行をブロックすることが出来る。 -- sleep_until() 実引数で渡した自国まで処理を中断する。 -- sleep_for() 実引数で渡した時間だけ処理を中断する。 ////////////////////////////////////////////////////////////////////////////// * 並行実行できる thread の数を取得する [#te2d6f95] namespace std { class thread { public: static unsigned int hardware_concurrency() noexcept; }; } - この関数は、現在実行している環境で並行実行出来るスレッド数を返す。 - この関数によって返される値はあくまで参考値であり、実際にどの程度並行実行出来るかは、実装や環境に依存する。 - 並行実行出来るスレッド数を計算できない場合や、実装によって正しく定義されていない場合、この関数は 0 を返す。 ////////////////////////////////////////////////////////////////////////////// * mutex [#mutex] namespace std { class mutex { public: void lock(); bool try_lock(); void unlock(); }; class recursive_mutex { public: void lock(); bool try_lock(); void unlock(); }; } - lock() -- オブジェクトに対しロックを掛ける -- 呼び出した時点で、同じオブジェクトに対し既にロックがかけられている場合、そのロックが解除されるまでブロックされる - try_lock(): -- ロックを試行する |~状態 |~try_loclk() 呼び出し結果 |~備考 | |どのスレッドもそのオブジェクトにロックをかけていない時 |ロックをかけ、true を返す | | |他のスレッドから既にロックがかけられている場合 |ロックに失敗し、false が返る | | - unlock() -- オブジェクトに掛けたロックを解除する //============================================================================ ** std::mutex [#std-mutex] - 再帰的にロックは掛けられない →1スレッド内で同じオブジェクトに2回ロックを掛けるとデッドロックが発生する。 - デッドロックにはまった場合、実装系がデッドロックを発見出来る場合には、 std::errc::resource_deadlock_would_occur を設定した std::system_error 例外が送出される可能性がある。 //============================================================================ ** std::recursive_mutex [#recursive_mutex] - 再帰的にロック出来る mutex - スレッド間で排他的だが、同一スレッド内からは同一オブジェクトに対し複数回のロックを掛けることが出来る。 -- ロックしたのと同じ回数アンロックすると、ロックが解除される。 - 何回までロックを掛けられるかは規定がない。上限を超えた場合の挙動は -- try_lock(): false が返る -- lock() : std::system_error 例外を送出 //============================================================================ ** std::timed_mutex [#timed_mutex] - タイムアウト付き mutex - lock() / try_lock() も使用出来る - タイムアウト指定出来るロック関数が用意されている -- try_lock_for() : 指定時間だけロックを試行する -- try_lock_until() : 指定時刻までロックを試行する -- タイムアウト付きロック関数は、タイムアウトまでにロック出来た場合には true, 出来なかった場合は false を返す。 //============================================================================ ** std::timed_recursive_mutex [#timed_recursive_mutex] - 再帰的にロックを掛けられる、タイムアウト付き mutex - std::recursivemutex, std::timed_mutex の両方の機能を備えている。 //============================================================================ ** std::shared_mutex [C++17] [#shared_timed_mutex] //============================================================================ ** std::shared_timed_mutex [C++14] [#shared_timed_mutex] ////////////////////////////////////////////////////////////////////////////// * リソースのロックを管理する [#o619d861] //============================================================================ ** std::lock_guard [#lock_guard] - ロックのインスタンスを生成し、それが有効な間をロックの対象とする - よって、明示的なロック解除の I/F は使わない #include <mutex> { std::mutex mtx; { std::lock_guard<mutex> lk( mtx ); // ここからロックが掛かる // 処理... } // lk の有効なスコープを抜け、lk のインスタンスが消滅すると同時にロック解除となる } //============================================================================ ** std::unique_lock [#unique_lock] - コンストラクタ第2引数に std::defer_lock 変数を渡すと、ロックのタイミングを遅延する - 後からロックを取得するには、 lock() / try_lock() を呼び出す - 使用する mutex が try_lock_for(), try_lock_until() メンバ関数を持っている場合、コンストラクタ第2仮引数に std::chrono::duration / std::chrono::time_point クラスのオブジェクトを渡し、タイムアウト付きでロック取得を試行出来る。 - コンストラクタに std::defer_lock が渡され、ロックを取得するタイミングが遅延されている場合、 try_loc_for() / try_lock_until() メンバを呼び出し、タイムアウト付きでロック取得を試行出来る。 - release() : 管理しているオブジェクトを手放す - unlock() : 任意のタイミングでロックを解除する - owns_lock() : 現在ロックを取得しているかどうかを確認出来る ////////////////////////////////////////////////////////////////////////////// * 複数のリソースをロックする [#w31f69a0] ////////////////////////////////////////////////////////////////////////////// * ロックせずに排他アクセスする [#j61ebc0e] //============================================================================ ** std::memory_order [#e66f6277] //============================================================================ ** std::atomic [#qf70be40] ////////////////////////////////////////////////////////////////////////////// * スレッドセーフに1度だけ関数を呼び出す [#g8f42eac] ////////////////////////////////////////////////////////////////////////////// * 条件変数を使用する [#kb3e1964] ////////////////////////////////////////////////////////////////////////////// * thread をまたいで値や例外を受け渡す [#std-promise-future] - スレッド間で非同期に値や例外を受け渡す方法として、 std::promise / std::future が提供されている //============================================================================ ** 使い方 [#std-promise-future-usage] + std::promise の実体生成し下記を呼び、その実体に対応する std::future の実体を取得する。 get_future() + 値と例外は、以下のいずれかを用いてどちらか一つを、''1回だけ''設定出来る。 set_value() set_exception() -- 値または例外が既に設定された状態で再度これらが呼び出された場合、 std::future_error 例外が送出される。 -- 対応する std::future が値や例外を受け取るタイミングを、このスレッドの終了時まで送らせたい場合は、以下のいずれかを使う set_value_at_thread_exit() set_exception_at_thread_exit() + std::promise の get_future() で取得した std::future の get() 関数を使い、std::promise で設定された値を受け取る。 -- 設定されたのが値ではなく例外だった場合は、例外が送出される。 -- まだ std::promise で値も例外も設定されていなかった場合、内部で wait() を呼び出す。 --- wait() は、値または例外の設定を待機するもので、設定されるまでは呼び出し元のスレッドをブロックする。 -- 値の設定待ちに条件を付けたい場合は、以下を使用する wait_for( std::chrono::duration d ) // 指定した時間 d だけ待機する wait_until( std::chrono::time_point tp ) // 指定した時刻 tp まで待機する これらの返り値は以下 std:future_status::ready // 指定時間内に値か例外が設定された std:future_status::timeout // 値も例外も設定されないまま指定時間を過ぎた std:future_status::deferred // 実行のタイミングが延期されている - ''std::future の get() を呼び出すと、 std::promise との対応関係は解除される。'' ''その状態で get() を呼び出した場合の動作は未定義である。'' //============================================================================ ** 使い方の例 [#std-promise-future-example] - 以下は、 std::string をやり取りする例: #include <iostream> #include <thread> #include<future> static std::mutex g_print_mutex; void LockPrintf( const std::string & str ) { std::lock_guard<std::mutex> lk( g_print_mutex ); std::cout << str << std::endl; } void ThreadFuncA( std::promise<std::string> pr ) { std::string sendstr ="from ThreadFuncA"; pr.set_value( sendstr ); // 他のスレッドに受け渡す値を設定 LockPrintf( "ThreadFuncA" ); } int main( int argc, char * argv[] ) { std::promise<std::string> prom; std::future<std::string> futu = prom.get_future(); // promise で他スレッドで設定された値を取得 std::thread th_a( ThreadFuncA, move( prom ) ); std::string future_string = "Rcv from A: " + futu.get(); LockPrintf( future_string ); th_a.join(); return( 0 ); } ////////////////////////////////////////////////////////////////////////////// * 非同期処理をする [#kd28f4df] ////////////////////////////////////////////////////////////////////////////// * スレッドローカル変数を使用する [#k24f952f]
テキスト整形のルールを表示する