以前作成した関数をスレッドにしてバックグラウンドで動かしたいと
思っているんですが、スレッドからのデータを受け取るにはどうすればいいのでしょうか?
関数は
function GetCRC(Filenane: string): string;
となってます。
Filenameをスレッドに渡すには、TThread.Createをoverrideすれば
できるとわかったのですが戻り値をうけとるにはどうすればいいのでしょうか?
こちらの環境はWindows2000 Delphi4です。
スレッドの仕組みはわかっていますか?
スレッドを生成する側(生成される側ではない)から見たとき、スレッドを開始した後すぐに処理は次に移ります。
このとき、スレッドはまだ終了していません。
どこでデータを受け取りたいんでしょうか。
データの種類にもよりますが、たとえばメインスレッド側のボタンが押されたときに、作成されたスレッドのCount値を取得したければ、スレッドにプロパティを持たせてそれを読み込めばよいかと思います。
作成されたスレッドが定期的にデータをメインスレッドに渡すのであれば、メインスレッドにメッセージをとばし、メインスレッド側でそのメッセージを処理させればよいと思います。
あと、終わったときにイベントを発生させて、そこで返すという方法もありますね。
どちらにしても、functionで返すという方法は無理そうです。
だって、考えてみてください。
にしのさんのいうとおり、スレッドは、関数を抜けたあとに終わりますよね?
関数がスレッドをが終わるまで待つとか、そんなことをしていたら、
結局メインスレッドでやるのと、何の代わりもなくなってしまいます。
"今回スレッドじゃなくしてやりたい"とか言うのなら、
Executeメソッド内の命令をコピーするしかないですね。
さもなくば、その内容をごっそり別ユニットに移して、
スレッドはそのユニットの命令を呼ぶ。
今回はスレッドを介さずに、ユニットの命令そのものを呼ぶ。
というふうにするしかないです。
> 終わったときにイベントを発生させて、そこで返すという方法もありますね。
??わけがわかんなくなってますね^^;
正確には、イベントを発生させて、
そのイベントのパラメータとして、結果を返す といったところですね。
お返事ありがとうございます。
スレッドを使うのは初めてなので、まだまだわからない部分があるのですが
確かに説明不足でした。
受け取りたいのは関数の実行結果です。
もともと、時間のかかること(今回の場合CRCを計算する)を実行させていると
フォームの移動や終了が出来ないのを何とかしたかったので、はじめは
計算途中でApplication.ProcessMessagesを実行していました。
しかしスレッドにしたほうがスマートかなと思い時間のかかる部分(関数)を
スレッド化しようと思いました。
メインスレッドは生成したスレッドの計算結果がでるまでひたすら待って、
その実行結果を受け取って次の処理に進むという感じになります。
今回の場合だとにしのさんが提示した「メインスレッドにメッセージをとばす」
ということで対応できるのかなとも思いますが、これは
どういういうことでしょうか?
こちらでも調べて見ますが、よろしければ回答をお願いします。
たかみちえさん、お返事ありがとうございます。
>スレッドは、関数を抜けたあとに終わりますよね?
確かにそうですね。終わってしまったら何も出来ないですね。
最初スレッドについて調べたとき、メインスレッドから生成するスレッドに
変数を使ってデータを渡すことが出来るということがわかったので、逆のこと、
スレッドからメインスレッドに変数を使ってデータを渡すことも出来るもんだと
思い込んでいました。
メインスレッドがデータを受け取るには
「メインスレッドにメッセージをとばす」
「イベントを起こす」
というやり方があるのですね。勉強になります。
Application.ProcessMessagesとスレッド化とは、本質的に違うものですから、お互い代用はできないと思いますよ。
ProcessMessagesは、今プロセス内(ソフト内)のウィンドウがメッセージキューに持ってるメッセージの処理などをやってくれますけど、
スレッドを使ったからといって、その間ウィンドウメッセージを処理してくれるわけではないはずです。
だからといって、待ち時間ずっと、メインスレッドでProcessMessagesを呼び出していては…、
何の意味もありませんね。
Sleep APIでも…意味なしです。
スレッドにしたほうがいい場合もありますけど、この場合、スレッドにはしないほうがよさそうです。
そもそもスレッドを作ると、CPU時間を多めに取ってしまうとのことなので、
必要なときでなければ、そのまま通してしまったほうがいいと思います。
スレッドのことについて、補足。
スレッドを分けるというのは、たとえば一つの作業を、二人の人が分かれてやるようなものです。
一人の人(Aさんとしましょうか)が作業をしていると、とても時間がかかってしまう。
だから、もう一人の人(Bさんとします)に、作業をいくつか分けて、やってもらいます。
この分けられた作業が、スレッドオブジェクトにかかれた命令文ですね。
ですから、AさんはBさんの作業を待ってるその間、当然何かをやっているべきなんです。
Aさんがその間暇をもてあましているんだとすれば、Bさんに作業を任せたりせずに、自分でやってしまったほうがいい そういうことです。
(そもそも、Bさんに作業を頼むという時間が、無駄になりかねません)
さて、データの受け渡し方法ですが。
AさんとBさんは、同じ場所で作業をしているわけではないとします。
たとえば、別々の電車に乗ってるとします。
AさんとBさんの間でなにか、ものをやり取りするには?というはなしです。
(ここで、"電話を使えばいいじゃん"とか、そういうのはナシで^^;)
このばあい、やっぱりどこか同じ駅で電車を止めて、そこでやり取りしなくてはいけません。
それが"同期"というものです。
つまり、Aさん(メインスレッド)とBさん(生成したスレッド)で、情報をやり取りするには、
いったん電車を、同じ駅で止めてもらうようなこと、"同期(Syncronizeメソッド)"が必要になる。というわけです。
電車が止まっている間ならば、AさんとBさんは自由にやり取りできます。
なので、その間に駅で呼び出し(イベントを起こす)するか、
あらかじめ場所を決めておいて、情報を渡す(スレッドのプロパティとする)か
とか、するわけです。
メッセージを飛ばすというのは、たぶんイベントを起こすというのとほぼ同じじゃないでしょうか?
普通のイベントは、メッセージを受け取ったオブジェクトが、対応したイベントを起こしているだけですから。
後半特に、悪い例でした(^^ゞ練ってなかったもので、ちょっとこじつけ気味かもしれません。
まあ、わかったでしょうか?
あとあとどこかに書くときには、もうちょっと推敲しておきます(-_-;)
単純に待つだけであれば、TThreadにWaitForがありますよ。
WaitForは、ずっと待ち続けるので、キャンセルしたいのであれば、
・スレッドのCreateで、FreeOnTerminateをFalseにする
・Executeの最後でTerminateメソッドを呼び出す。
・スレッドに、CRCプロパティを準備。Execute実行時に値をセットする。
・メインスレッドで以下のようなコードで処理。
Thrd := TCRCThread.Create(Text);
while not Thrd.Terminated do
begin
Application.ProcessExecute;
(* キャンセルされたか調べる *)
end;
ThisCRC := Thrd.CRC;
Thrd.Free;
にしのさん、お返事ありがとうございます。
時間のかかる部分をスレッドにして、にしのさんに教えてもらった
方法を試してみました。
WaitForだとメインスレッドが止まってしまうんですね。
フォームの移動なども出来なくなってしまいます。
それで、その次のやり方を試してみたんですが、これはエラーが出て
動かなかったです。ちょっと原因がわからないんですけど
これから調べてみます。
>レッドに、CRCプロパティを準備
というのは
生成されるスレッドのtypeのところで
public
CRC: string;
と定義すればいいんですよね?
とりあえずご報告まで。
propertyをヘルプでみてください。
Delphi的には、
public
CRC: String;
とするよりも、
private
FCRC: String;
public
property CRC: String read FCRC;
とすべきでしょう。
書き込みが必要であれば、
private
FCRC: String;
public
property CRC: String read FCRC write FCRC;
とするか、
private
FCRC: String;
procedure SetCRC(Value: String);
public
property CRC: String read FCRC write SetCRC;
として、SetCRCで、引数Valueが正しいCRCかどうかを判別し、正しければFCRCに代入するようにします。
ツイート | ![]() |