実践C++応用講座Qt編こんにちは。前回はSignal/Slotのごく基本であるコールバック関数的な使い方を解説しました。双方向依存を断ち切るのに有用な方法でしたね。Signal/Slotは更に強力です。(強力過ぎて頭痛い程)それはお手軽だけど実は本格的にスレッド間通信できるからです。Qtのスレッド間通信(というか非同期プログラミング)の要と言っても過言ではないと思います。今回はその基本部分を解説します。その後2回ほどかけて深堀りしていきます。2つの仕組みがあるSignalが発行されたらSlotが呼ばれます。これだけなら、普通は「1Signalを発行(emit)するとその場から直接Slot関数が呼ばれる」と理解するのではないでしょうか?そのように振る舞うこともありますが、そうではないこともあります。「2Signalを発行(emit)するとキューへエンキューされ、それをデキューしてSlot関数が呼び出される」という仕組みも存在しているのです。前者のみを使う分にはコールバック関数を登録する仕組みとして理解しておけば十分です。マルチスレッドでプログラム作成する時に後者の機能が超便利なのです。スレッド間で関数呼び出しを排他制御に煩わされることなく行えるというマルチスレッド・プログラム開発時には非常にありがたい仕組みなのです。(逆に、シングル・スレッドで十分な場合はあまりメリットを感じることはないかも知れません。スレッド間通信としての応用マルチスレッド・プログラミングする際、各スレッドが完全に独立して動作することは少なく、多くのケースではスレッド間での通信が必要になります。その方法は多数あるのですが、キューを介して通信したいことがあります。メイン・スレッドからサブ・スレッドへ要求し、結果をサブ・スレッドがメイン・スレッドへ返却するという流れは比較的よく使われると思います。その際にメイン・スレッドからサブ・スレッドへ要求する場合にサブ・スレッド用のキューへエンキューし、逆も同様という方法は非常に面倒でコーディングとデバッグの手間がたいへんなのですが、比較的よく使われます。(手抜きしてもっと簡単な方法で開発を初めても、プロジェクトの進行に伴い、キューを使わないと解決できない問題が出てくることは少なくありません。)Qtの場合は、そのようなケースに至る前に最初からキューを使っておけば良いのです。Signal/Slotを使うと既にキューが「裏」で実装されており、キュー周りのコーディングとデバッグは不要なのです。問題はその裏側の仕組みを把握してきちんと設計することです。(ここ2~3回でそのあたりまで含めて解説する予定です。)2.具体的にSinal/Slotの関係は上述したように大きく2つあります。キュー経由する方の仕組みは2つあります。1Signal発行(emit)するとその場から直接Slot関数が呼ばれる2Signalを発行(emit)するとキューへエンキューされ、それをデキューしてSlot関数が呼び出される2-1)Signal発行時エンキューしたら直ぐに返ってくる(Slot関数の終了を待たない)2-2)Signal発行時Slot関数の終了を待って返ってくる1の場合、キューがないので当たり前ですが「エンキューしたら直ぐに返ってくる」は存在しません。常にSlot関数が終わってから返ってきます。2-1)がよく使われます。リスクもほぼないのでできるだけこちらを使った方が良いです。Signal発行側で確保したメモリ(多くはローカル変数でスタック上に確保するでしょう)に何かデータをセットしてSlot関数で処理させたいことが時々あります。その時にemitする際に当該メモリをコピーしてSlotへ渡せば2-1)で十分ですが、データ量が大きいような時コピーしたくないことがあります。そのような時は2-2)の仕組みを使って、Slot関数がメモリへのアクセスを終わってから呼び出し側がメモリを開放すれば良いです。リスクはデッドロックです。要求元が2-2)で要求を出し、要求先がSlot関数内で2-2)で応答を返却しようとするとあっさりデッドロックします。意外に危険なのでなるべく2-2)は使わない、使う場合はデッドロックしないことを慎重に検討下さい。(例えば、要求元は必ず2-1)で要求し、要求先が2-2)で応答を返すなど。)これらはconnect関数の最後のオプションで指定します。Qt::BlockingQueuedConnection前回解説したconnectの3種類全部について、それぞれ上記の3種類が存在するので合計9通りのSignal/Slotの接続が存在します。3.サンプル・ソース今回は単純なコンソール・アプリを作ってみました。blockingQueued)返却後、最後にfinished応答返却してメイン・スレッドはアプリを終了させています。応答を受け取ったメイン・スレッド側は、それぞれのSlot関数で単純に1秒待ちしてSlot関数を終了しています。サブ・スレッド側はSignal発行の前後で「現在時刻」を表示していますので、2-1)QueuedConnectionと2-2)BlockingQueuedConnectionの相違について時刻を見て振る舞いを把握してみて下さい。今回のサンプルの主要部分のソースです。全体はGistにアップロードしていますので参考にして下さい。(Windowsでテストしています。~SubThread()[/text]4.まとめ今回はQThreadと絡む部分の基本的な振る舞いを解説しました。実は「2.具体的に」で解説したオプションは後2つあります。そのうちの1つのQt::AutoConnectionが実は最もよく使われます。いいますか、このオプションは省略可能で省略時はQt::AutoConnectionなのです。これはQt::DirectConnectionとQt::QueuedConnectionを自動的に選びます。SignalとSlotが同じスレッドに属していたらQt::DirectConnection、そうでなければQt::QueuedConnectionです。簡単なようにみえますね。実は非常に奥が深いです。Slot関数が属するスレッドって何?って話です。普通は当該関数を呼び出したスレッドが「属するスレッド」です。その点、Signalをemitした時のスレッドがSignalが「属するスレッド」なのでSignalは簡単です。Slotは呼び出される前に「属するスレッド」が決定されます。この辺りがQtのSignal/Slotの難しい部分です。次回(もしかしたら次々回も含めて)に解説する予定ですのでしばしお待ち下さい!それでは今回はこの辺で。コメントをキャンセルメールアドレスが公開されることはありません。テクノロジーの登録商標です。technology.。