wxWidgets でマルチスレッドプログラミング
java.SwingでもRunnableをimplementsしたクラスを実装すれば、マルチスレッドでプログラミングを行うことができます。
wxWidgetsでも同じようにwxThreadクラスを継承したクラスを実装すれば、マルチプラットフォームを実現しつつ、マルチスレッドでプログラミングすることができます。
wxThread クラス
マルチスレッドを扱うことができます。
クラスの宣言
このようになります。
class c_thread:public wxThread{ public: c_thread(); //コンストラクタ private: virtual ExitCode Entry(); //スレッドのエントリーポイント };
最小設計はこんな感じになります。
スレッドで実行する内容はEntry関数に記述していきます。
wxThreadのコンストラクタには、二つの定数を指定することができます。
名前 | 意味 |
---|---|
wxTHREAD_DETACHED | detachedなスレッドを作成する。(デフォルト) |
wxTHREAD_JOINABLE | joinableなスレッドを作成する。 |
c_thread::c_thread() : wxThread(wxTHREAD_JOINABLE){ }
detachedとjoinableの大きな違いは、detachedなスレッドは勝手に自分をdeleteしてくれますが、joinableなスレッドは明示的にdeleteする必要があります。
作ったクラスのインスタンス化と事前準備
インスタンスは通常のクラスと同じようにnewします。
また、コンストラクタを実行しただけではスレッドが作成されることがないのでwxThread::Create()を実行する必要があります。
c_thread *thread = new c_thread(); //コンストラクタが実行されただけでスレッドは作成されていない。 if(thread->Create() != wxTHREAD_NO_ERROR) return -1;
またCreate関数では以下の定数のうち一つが戻り値として返却されます。
名前 | 意味 |
---|---|
wxTHREAD_NO_ERROR | エラーが発生しなかった。 |
wxTHREAD_NO_RESOURCE | リソースが不足している。 |
wxTHREAD_RUNNING | スレッドが実行中である。 |
スレッドの実行内容の実装と実際に実行させる
スレッドの実行内容はEntry関数の中に書いていくことになります。
int ans; wxThread::ExitCode c_thread::Entry(){ Sleep(5000); return (wxThread::ExitCode)0; }
サンプルでは5000ms(5秒)待機して終了しています。
whileなどでずっと処理を回したい場合などでは、TestDestroyを使用する必要があります。そうしなければ、スレッドが正常に終了しない場合があるからです。TestDestroyがtrueを返した場合はそのスレッドは直ちに終了させる必要があります。
while(!TestDestroy()){ //行いたい処理 }
TestDestroyの詳細については後ほどまとめます。
以上で、実装ができたので、実行していきます。
実行は、任意の場所でwxThread::Run()を実行するだけです。
thread->Run();
こうすることで自動的にEntry関数が呼ばれ、スレッドの内容が実行されています。
また、スレッドの終了を待つ場合は、joinableなスレッドで宣言した場合のみ、Wait関数を使うことで調べることができます。
thread->Wait(); //戻り値はwxThread::ExitCode(いわゆるEntry関数での戻り値)が帰ってきます。
スレッドの一時中断とプログラム終了時の処理
wxThread::PauseとwxThread::Resumeでスレッドのサスペンド/復帰を実行できます。
thread->Pause(); //スレッドのサスペンド thread->Resume(); //スレッドをサスペンド状態から復帰させる。
前述にTestDestroyがでましたが、あれはこのPauseやResume、終了処理での時に必要になります。
終了処理はjoinableなスレッドと、detachedなスレッドとで若干変わってきます。
ただし、言えるのはどちらもDelete関数を使用してスレッドを終了させる必要があります。
Delete関数はスレッドに対してスレッドを終了させるように要求します。この時TestDestroyを記述していない場合は、要求してもスレッド側で要求を処理していないのでスレッドが終了しないことになります。
Kill関数で無理やりにスレッドを終了させる方法もありますが、スレッドのリソースが解放されないなどの問題があるため推奨されません。
joinableなスレッドな場合はwxThread::Deleteを実行して終了要求をしたあと、wxThread::Wait関数でスレッドの終了を待ちます。
また、joinableなスレッドは自動でdeleteされないので明示的にdeleteさせる必要があります。
void OnExit(wxCommandEvent& event){//終了関数などでの実行が望しい。 thread->Delete(); //終了要求 thread->Wait(); //スレッドの終了待ち delete thread; //自動ではしてくれないので、明示的にdeleteする必要がある。 }
detachedなスレッドではwxThread::Wait関数が使用できないので、wxThread::IsRunning関数を使います。
また、detachedなスレッドではすでに終了したスレッドに対してDelete要求はできません。プログラムがクラッシュする原因になります。(detachedなスレッドはDelete要求をすると自動でdeleteもするため)
void OnExit(wxCommandEvent& event){ thread->Delete(); //終了要求 while(thread->IsRunning()) //スレッドが実行中であればtrueを返す。 wxThread::This->Sleep(100); //スレッドが実行中ならばすこし待機する。wxThread::Thisは呼び出したスレッドのオブジェクトを返す。 //delete thread; detachedなスレッドでは不必要。 }
総評
作成できるスレッドはdetachedなものと、joinableなものがありますが、Wait関数が使えるjoinableなスレッドの方が扱いやすいのではないかと思います。(終了処理も楽ですし。)
また別のスレッドからメインのGUIクラスに対してはアクセスすることができません。java.Swingと同じように。
そのため、スレッドから変更を行いたい場合はwxQueueEvent関数等でメインのスレッドにイベントをポストして間接的変更する方法があるようですが、当方の環境でためしたらイベント型についてコンパイルエラーが発生したため詳しくはわかりませんでした。
グローバル変数等の複数スレッドからアクセスされる恐れがあるものについてはCriticalSectionやMutexを使います。こちらも詳しくはためしていません。
それよりも簡単にGUI変更を容易にするためにjava.SwingでいうSwingWorkerのようなもの、wxThreadHelperというクラスが存在します。
そのことについては別の記事で紹介したいと思います。
HTTP通信等もwxWidgetsでサポートされているのでいつか紹介できればなと思っています。
サンプル->thread.cpp