これは私がはまった落とし穴を再現するデモ・プログラムです。
test.cpp
#include <thread>
#include <iostream>
#include <unistd.h>
#include <atomic>
using namespace std;
class my_service
{
public:
void run() {
cout << "run()" << endl;
worker_ = thread([this]() {
cout << "thread started." << endl;
canceled_ = false;
for (int i = 0; i < 10 && !canceled_; i ++) {
cout << i << endl;
sleep(1);
}
});
}
void stop() {
cout << "stop()" << endl;
canceled_ = true;
worker_.join();
}
private:
thread worker_;
std::atomic<bool> canceled_; // atomic<T>型に修正しました
};
int main()
{
for (int i = 0; i < 2; ++ i) {
my_service service;
service.run();
sleep(0); // [ここに注目]
service.stop();
}
return 0;
}
実行結果
run()
stop()
thread started.
0
1
2
3
4
5
6
7
8
9
run()
stop()
thread started.
0
1
2
3
4
5
6
7
8
9
あくまで説明用なので、便利なライブラリーは使わず直接std::threadを使っています。
main() 関数の中で run() して 直後に stop() しているため、スレッドは1回も実行されないつもりでしたが、実は スレッドが実行開始される前にstop()が走ってしまうため、期待通りの動作になりません。
sleep(0)をsleep(1)とかに変えるとちゃんとstop()でスレッドが止まります。
スレッドの処理内容によっては最悪デッドロックになることもあり、実際のプログラム中では気づきにくいので要注意です。
ビルド方法
g++ -std=c++11 -Wall -pthread -g test.cpp
私なりに問題を解決したバージョンを作ってみました。
(このコードは間違っています。詳しくはコメントをご覧ください。)
test2.cpp
#include <thread>
#include <iostream>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <unistd.h> // for sleep
using namespace std;
class my_service
{
public:
void run() {
cout << "run()" << endl;
std::unique_lock<std::mutex> lk(m);
std::condition_variable cv;
worker_ = thread([this, &cv]() {
cout << "thread started." << endl;
canceled_ = false;
cv.notify_one();
for (int i = 0; i < 10 && !canceled_; i ++) {
cout << i << endl;
sleep(1);
}
});
cv.wait(lk); // ここは間違っているのでマネをしないでください!
}
void stop() {
cout << "stop()" << endl;
canceled_ = true;
worker_.join();
}
private:
thread worker_;
std::mutex m;
std::atomic<bool> canceled_;
};
int main()
{
for (int i = 0; i < 2; ++ i) {
my_service service;
service.run();
sleep(0);
service.stop();
}
return 0;
}
実行結果
run()
thread started.
stop()
run()
thread started.
stop()
実行結果を見ると、処理の順番がかわっています。
コメントありがとうございます。
つっこみ歓迎です!