まえふり
初めて仕事でC++を書くことがあって、特殊なことにC++98とC++11の環境がありました。
双方でどういう風にスレッド処理が実現できるのか分かったのでメモとなります。
環境はMacでしたがC++のコマンドはLinuxベースなのでUbuntuでも同じコマンドじゃないかなと思います。
Winの場合は、Visual StudioでC++開発ができるようなので、そちらで実行してみてくださいm(_ _)m
やったこと
もちろん仕事内容は書けないのでサンプルを使ってまとめます。
例えば、メインスレッド(もしくはUIスレッドなど)で処理が一切止まらないように裏側で別の処理を走らせてその結果を定期的にメインスレッドへ更新させるサブスレッドを作りたいケースが出てくると思います。
※Galaxy Note 8でささっと書いたので絵の上手い下手はご了承ください><
さて、これをC++で実装するにはどうしたら良いのか。
調べるとC++のバージョンによってサポートしているライブラリが異なり、C++98だとpthread、C++11だとstd::threadを使うことになると分かりました。
参考の記事は最後にまとめるので、まずは話しを進めていきます💪✨
サンプルについて
分かりやすいようにサブスレッドでは単純な計算をする処理を実行させ、その計算結果を表示させる処理をメインスレッドで実装しました。
Webやスマホのアプリ開発でもMVC/MVVMのモデルパターンで開発していくとほぼ同じような設計になると思うのでイメージしやすいのではないでしょうか。
C++98
/*
* PthreadSample.cpp
*/
# include <stdio.h>
# include <pthread.h>
# include <unistd.h>
void subMethod(void* pArg)
{
printf("subMethod start \n");
int *pVal = (int*) pArg;
for(int i = 0; i < 3; i++)
{
sleep(1);
*pVal *= 2;
}
printf("subMethod end \n");
}
void *subThread(void* pArg)
{
printf("subThread start \n");
int *pVal = (int*) pArg;
*pVal = 200;
sleep(3);
subMethod(pVal);
printf("subThread end \n");
return 0;
}
int main()
{
pthread_t handle;
int data = 100;
printf("main init [%d]\n", data);
pthread_create(&handle, NULL, subThread, &data);
for(int i = 0; i < 10; i++)
{
sleep(1);
printf("main now [%d]\n", data);
}
pthread_join(handle, NULL);
return 0;
}
> clang++ ./PthreadSample.cpp -g -o ./PthreadSample.run
> ./PthreadSample.run
main init [100]
subThread start
main now [200]
main now [200]
subMethod start
main now [200]
main now [400]
main now [800]
subMethod end
subThread end
main now [1600]
main now [1600]
main now [1600]
main now [1600]
main now [1600]
C++11
/*
* ThreadSample.cpp
*/
void subMethod(int *pVal)
{
printf("subMethod start \n");
for(int i = 0; i < 3; i++)
{
sleep(1);
*pVal *= 2;
}
printf("subMethod end \n");
}
void subThread(int *pVal)
{
printf("subThread start \n");
*pVal = 200;
sleep(3);
subMethod(pVal);
printf("subThread end \n");
}
int main() {
using namespace std;
int data = 100;
printf("main init [%d]\n", data);
thread handle(subThread, &data);
for(int i = 0; i < 10; i++)
{
sleep(1);
printf("main now [%d]\n", data);
}
handle.join();
handle.detach();
return 0;
}
> clang++ -std=c++11 -stdlib=libc++ -Weverything ./ThreadSample.cpp -g -o ./ThreadSample.run
> ./ThreadSample.run
main init [100]
subThread start
main now [200]
main now [200]
subMethod start
main now [200]
main now [400]
main now [800]
subMethod end
subThread end
main now [1600]
main now [1600]
main now [1600]
main now [1600]
main now [1600]
まとめ
動かしてみると分かりますが、上記の実行結果とはたまに異なることがあります。
それは、サブスレッドにしたことで処理の同期をしていないこととその時のCPU処理によって状況が変わるからです。
動きは同じ感じですが実装面だとpthread
に指定するメソッドはstatic
でないと指定できません。
上記の場合、static
ではありませんが、ヘッダークラス.h
でメソッドを定義した際にstatic
指定にし、.cpp
側では上記のようにstatic
指定無しでも大丈夫なようです。
ここで重要なのは、引数で渡すメインスレッドとサブスレッドで扱う変数をポインタで渡しているのですが、この取り扱いには十分注意が必要です。
時に何かの処理で値を代入した際にNULL
が入ってしまいエラーでアプリが止まってしまうケースが出てくると思います。
必ず例外処理や同じ資源を扱う際の管理方法もちゃんと決めて実装することです。