std::thread/boost::threadは、コンストラクタでスレッドの実行を開始するので、あとから実行しようと思ったらどうすればいいのか。ムーブセマンティクスがなかった頃、Boost.Threadではshared_ptrと組み合わせることでこれを実現していました。
#include <iostream> #include <boost/shared_ptr.hpp> #include <boost/thread.hpp> class X { boost::shared_ptr<boost::thread> thread_; void do_() { std::cout << "do" << std::endl; } public: ~X() { if (thread_) thread_->join(); } // あとで実行(shared_ptr::resetでthreadを作る) void start() { thread_.reset(new boost::thread([this] { do_(); })); } }; int main() { X x; x.start(); }
ムーブセマンティクスに対応してからは、std::thread/boost::threadは、以下のようにして「あとから実行」を簡単にできるようになりました:
- thread型オブジェクトをデフォルトコンストラクトする
- あとから実行する際に、実行する関数をコンストラクタに指定したthread型の一時オブジェクトを作り、ムーブ代入する
これで、開始したスレッドの所有権を、デフォルトコンストラクタしたいわば空のthreadオブジェクトに移譲することができ、それによってスレッドをあとから実行することができます。
#include <iostream> #include <vector> #include <thread> class X { std::thread thread_; // デフォルトコンストラクト void do_() { std::cout << "do" << std::endl; } public: ~X() { if (thread_.joinable()) thread_.join(); } void start() { // 開始したスレッドを持つthread型一時オブジェクトをムーブ代入 thread_ = std::thread([this] { do_(); }); // もしくはthreadオブジェクトを作ってから明示的にmove // std::thread t([this] { do_(); }); // thread_ = std::move(t); } }; int main() { X x; x.start(); }
Boost.Threadは、Boost.Moveが入るだいぶ前から自前のムーブ機構を持ってるので、このイディオムを使用することができます:
参照: boost::threadはC++03/C++0xの両方でmove対応してる
追記 {
ムーブ使わなくても昔からswapができました。
}
さらに、C++03までの標準コンテナは要素型に対して「コピー可能であること」を要求していたのに対し、C++11の標準コンテナは「コピー可能またはムーブ可能であること」という要求になったため、コピー不可なthreadクラスのオブジェクトもコンテナに持つことができます。
#include <iostream> #include <thread> #include <mutex> #include <vector> #include <string> #include <algorithm> std::mutex mutex_; void print(const std::string& s) { (std::lock_guard<std::mutex>(mutex_)), (std::cout << s << std::endl); } void f() { print("f"); } void g() { print("g"); } int main() { std::vector<std::thread> threads; threads.emplace_back(f); threads.emplace_back(g); // もしくはthreadオブジェクトを作ってムーブ // threads.push_back(std::thread(f)); // threads.push_back(std::thread(g)); std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); }
Boost.Threadには、ムーブ対応してなかった頃のためか、std::list
Boostで「thread_groupはまだ必要?」という議論が行われてる最中です。
ちなみに、標準コンテナの「コピー不可だけどムーブは可能」という型を受け入れるという仕様は、スレッドだけじゃなく多くの場面で役立ちます。UIを作る場合、ボタンやウィンドウなどのクラスは多くの場合コピー不可で設計されています。他にも、通信で使用するソケットクラスや、シリアライザーのようなファイルリソースを内部に包含するクラスもコピー不可である場合があります。これらをコンテナに持つ際に、C++11のコンテナに対するムーブ拡張は非常に役立ちます。