マルチスレッドでたくさんの仕事を一度にこなしたい (後編)
ってなわけで、前回からだいぶ時間が経ってしまいました・・・。 期待してくださっていた稀有な方々に平身低頭のエイタです。
前回はマルチスレッド処理を行う上で必要な関数たちについて説明しましたが、憶えていますか? SharedObjectRegister、SharedObjectGet、SharedObjectUnregister でしたね。
今回はそれらを使って実際にサンプルプログラムを作ってみるというお約束をしましたが、正直後悔してます。 あの時はまさか、自分自身がこんなに忙しくなってネコの手を借りたくなる状況に陥るとは思っていなかったんです・・・。
実際、このブログもネコに手伝ってもらうつもりでしたが、この時期コタツから出てきてくれるようなネコはなかなか少ないようで、うまく調達することができませんでした。
まあ、それも仕方のないこと。 一人頑張って書いていきます。
準備
まずは、検索する 2 つのデータウィンドウがレイアウトされた画面を作成しました。
例によって芸のない画面レイアウトですが、ボタンをクリックすると両方のデータウィンドウを検索するという簡単な作りです。
[Single] ボタン Clicked イベント
dw_1.Retrieve() dw_2.Retrieve()
dw_1 (左) の検索が終わったら dw_2 (右) の検索が行われるという流れですが、2 つのデータウィンドウの検索を並行して行う処理を [Multi] ボタンに追加していきます。
2 つのオブジェクトを作成する
まず、検索結果を受け取りデータウィンドウにセットするためのオブジェクトを作成します。
前回も少し触れましたが、共有オブジェクトではグラフィカルなコントロールを直接参照することできないので、画面に配置したデータウィンドウと共有オブジェクトとを橋渡しするオブジェクトを挟まなければいけません。 ここではその橋渡し役に uo_callback というユーザーオブジェクト(カスタムクラス)を用意します。
このオブジェクトには、
- 並列処理を行う検索用オブジェクトに対象データウィンドウの DataObject を伝える
- 並列処理を行う検索用オブジェクトから検索結果を受け取る
- 受け取った検索結果画面のデータウィンドウに取り込ませる
という役目を与えます。
早速インスタンス変数に検索対象のデータウィンドウを定義します。 これにより共有オブジェクトがどの DataObject を利用して検索するか、また検索結果をどのデータウィンドウに反映するかを呼び出し元から指定することができます。
[インスタンス変数の宣言]
datawindow idw
さらに、上記の役割を果たす 3 つの関数を作成します。
of_setdatawindow ( datawindow adw )
//検索対象データウィンドウを設定する
idw = adw
string of_getdataobject ( )
//検索対象データウィンドウの DataObject 名を返す return idw.dataobject
of_setdata ( blob ablb_data)
//画面の DW に検索結果を適用する
idw.SetFullState(ablb_data)
次に実際に検索を行うための共有オブジェクト uo_sample を作成し、関数を作成します。 引数には先ほど作成した uo_callback を値渡し (value) で定義します。 こちらのオブジェクトもカスタムクラスで作成してください。
of_retrieve ( uo_callback auo_callback )
datastore lds transaction lt_trans blob lblb_fullstate lds = create datastore // uo_callback から DataObject を取得し DataStore に設定 lds.dataobject = auo_callback.of_getdataobject() lt_trans = create transaction //接続情報を設定 lt_trans.DBMS = "ORA Oracle" lt_trans.ServerName = "TestServer" lt_trans.LogId = "TestUser" lt_trans.LogPass = "pasuwado" CONNECT USING lt_trans; // DataStore にトランザクションを設定 lds.SetTransObject(lt_trans) // 検索 lds.Retrieve() // 全データとその状態を Blob に格納 lds.GetFullState(lblb_fullstate) // CallBack 用のオブジェクトに渡す auo_callback.of_setdata(lblb_fullstate) //接続終了 DISCONNECT USING lt_trans;
uo_callback から取得した DataObject を使って DataStore で検索した結果を GetFullState 関数で Blob 型に格納しています。 Blob 型にすることで、共有オブジェクトとデータウィンドウの連携が可能になります。
注意しなければいけないのが、非同期処理であるため「トランザクションが引き継げない」ということです。 このためトランザクションはこの関数内で作成して DB に接続しています。
共有オブジェクトとして登録し、呼び出す
さて、呼び出し処理です。
メインの処理(画面)とは別に2つの検索処理を並行で行うため、検索用オブジェクトのインスタンスを 2 つ作成しています。
[Multi] ボタン Clicked イベント
uo_callback luo_callback1, luo_callback2 uo_sample luo_sample1, luo_sample2 dw_1.Reset() dw_2.Reset() // uo_callback をインスタンス化 luo_callback1 = create uo_callback luo_callback2 = create uo_callback // uo_callback に検索対象のデータウィンドウを設定 luo_callback1.of_setdatawindow(dw_1) luo_callback2.of_setdatawindow(dw_2) // 共有オブジェクトを登録 SharedObjectRegister("uo_sample", "sample01") SharedObjectRegister("uo_sample", "sample02") // 共有オブジェクトを取得 SharedObjectGet("sample01", luo_sample1) SharedObjectGet("sample02", luo_sample2) // 検索処理呼び出し luo_sample1.post of_retrieve( luo_callback1 ) luo_sample2.post of_retrieve( luo_callback2 ) // 共有オブジェクトの登録を解除 SharedObjectUnregister("sample01") SharedObjectUnregister("sample02")
並列で行う処理は POST 呼び出し。 忘れないように・・・。
結果は如何に・・・?
まずは並列処理をしない場合の処理時間を計測してみましょう。 上記の処理以外に時間計測用の処理を追加しています。 アプリケーションを実行して [Single] ボタンクリック!
14 秒弱。 急いでいるときにはそこそこイラっとするくらいのタイムです。
次はいよいよ並列処理を実行してみましょう。 このイライラ、解消なるか?!
[Multi] ボタンをポチっとな・・・。
速くなった!
2 つの検索を並行で処理することにより、検索終了するまでの時間が 7 割程度になりました。 もちろんどれだけ速くなるかは DB サーバーの性能やネットワーク環境の状態などによって前後します。
結果的に「劇的に」とまでは言えないかもしれませんが、ストレスも 3 割減です! (※個人の感想です)
まとめ
いかがでしたでしょうか。
ちょっと複雑で説明不足感は否めませんが、PowerBuilder でもこんなことができるってことは憶えておいて損はないかもです。 働き方改革が叫ばれている昨今、少しの時間でも大切にして効率よくお仕事に励みたいですよね!
・・・とはいえ、いくらアプリケーションが速くなってたとしても、マルチスレッドが実装できない人間という生き物は夥しい仕事を前にいずれは猫の手を借りるしかないのです。 きっと手を貸してくれるネコも探せば見つかるでしょう(時期にもよると思いますが)。
でも実際、仕事場にネコがいたら逆に仕事にならないよね!
一日中ネコと戯れちゃいそうな猫派のエイタでした。