スレッドがマウスイベントで止まらないようにするには?


とじ  2011-03-10 04:20:12  No: 40224

こんにちは、

Delphi 7で作成したアプリをDelphi 2007へ移植しております。
スレッドを作成してシリアルデータ受信を行っていて、メインスレッドでデータ処理を行っています。一つ問題がありまして、ボタン上にマウスをかざす事でヒント表示を行うところで原因不明の動作をします。

メインスレッドをデータ処理用として、データ受信用スレッドを作り動作させているのですが、メイン画面上に複数あるTToolButtonボタンの中で、EnabledプロパティーがFalseとなっているボタンの上にマウスを移動させると、ヒント表示が空白となり、受信スレッドが止まってしまうのです。EnabledプロパティーがTrueとなっているボタンはマウスをかざして問題なくヒント表示が行えスレッドも動作し続けます。メモリ漏れかなと思いましたがそうではないようです。(アプリ終了後に漏れ表示するオプション付けてみました。)

以下スレッド実行部分です。解決につながるヒントを頂けませんでしょうか?

procedure TForm2.Execute;
begin
  while not Terminated do
  begin
    if Form1.CommErr=false then GetData;
    Application.ProcessMessages;
    sleep(5);
  end;
end;

環境は、Vista日本語版SP2、Delphi 2007 (December 2007 Update済)です。

よろしくおねがいします。


KHE00221  2011-03-10 05:05:45  No: 40225

とりえあえず
どこがスレッド?


とじ  2011-03-10 19:00:53  No: 40226

KHE00221様、
説明が足りませんでした。

スレッドはUnit2.pasへ書きました。
メインのフォームUnit1.pasでは、

uses
  ..., Unit2, ...

type
  TForm1 = class(TForm)
..
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    ToolButton5: TToolButton;
    ToolButton6: TToolButton;
...
....
  private
    Thread1: TFrom2;

TForm1.FormShow()プロシージャで
...
  Thread1 := TFrom2.Create(True);
  Thread1.Priority := tpLowest;
  Thread1.Resume;

でTFrom2を起動しています。Form1の表示と同じタイミングでForm2が動いています。

また、Unit2.pasでは最初の投稿で書いた以外に、
type
  TForm2 = class(TThread)
  private
    ....
    function GetData: boolean;
    ....
  protected
    procedure Execute; override;
  public
    ...
  end;

としています。

さらに、ToolButton1をクリックして、
  procedure TForm1.ToolButton1Click(Sender: TObject);
  begin
    if start.Enabled = false then exit;
    StartProcess(self);
  end;
を行います。

startと書いているのは、dfmで
      object ToolButton1: TToolButton
        Left = 120
        Top = 0
        Action = Start
        ParentShowHint = False
        ShowHint = True
      end
として、TActionのOnExecuteイベントを使っている為です。

このボタンのヒントは、
TForm1.FormCreate()プロシージャで
  Start.Hint := 'スタート';
としています。

StartProcess()では、
Start.Enabled:=False
として、ボタンを利用不可にしていますが、ここでマウスポインタが上にくると、ヒントが空白になりForm2が止まる(一時停止)する現象が出ます。マウスポインタをボタンから外せば、Form2は再開します。

他のボタン(ToolButton2〜6)でもTActionのOnExecuteイベントを使用しており、EnabledがTrueの場合は、上記現象が出ません。

From2がなぜ一時停止してヒントも空白表示になるのか原因を突き止めていません。


tor  2011-03-10 20:14:37  No: 40227

> TForm2 = class(TThread)
スレッドなのにTForm2という名前なのですね
とりあえずその人がApplication.ProcessMessagesするのはおかしい
(ワーカスレッドがメッセージポンプする必要はない、と言うかしてはいけない)ので外してみてください


とじ  2011-03-10 22:53:28  No: 40228

tor様、

ありがとうございます。
ご指摘のApplication.ProcessMessagesをコメントアウトして試したところ、状況は変わらないようです。
現象を良く見てみたところ、EnabledがFalseとなっているボタン上にマウスポインタを持ってくると、一瞬(0.3秒?)ですが本来表示すべきヒントが表示されるようです。その後は、一瞬見えた文字列は消えてしまい、マウスポインタが上にいる限りは、ヒント表示の枠だけが表示され、Form2スレッドが一時停止している状態です。


とじ  2011-03-10 23:01:46  No: 40229

tor様、

>> TForm2 = class(TThread)
>スレッドなのにTForm2という名前なのですね

名前が紛らわしいのですが、Unit2.pasでは
type
  TForm2 = class(TThread)
  private
    ....
    function GetData: boolean;
    ....
  protected
    procedure Execute; override;
  public
    ...
  end;

以外に、

type
  TForm2Thread = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    DatWriteFlag: integer;
    DatReadFlag: integer;
    ...
    ..
  public
  end;

を定義しています。

FormCreateでは通信ポートを開けて、FormDestroyではポートを閉じる処理をしています。


monaa  2011-03-10 23:52:40  No: 40230

もう訳がわからない状態っぽいので、プログラムを整理しなおしては如何ですか?

メインスレッド
  フォーム+ボタン (スレッドの開始、停止)
  データ処理ルーチン (受信データをCaptionに表示)
データ受信スレッド
  データ受信 (受信したらメインフォームにデータを転送)

という最小構成をつくり直してみてください。
スレッドでは行ってはいけない動作というのがありますので
その点を要チェックしてみてください。特にメインフォームのVCLにアクセスしたりしてはいけません。
あとスレッドオブジェクトの生成破棄の管理もポカミスが多い箇所ですので注意が必要です。
データの受信があまりにも多い場合は、そのたびにメインスレッドを呼びだすとメインスレッドの動作は重くなります。とりあえず、メインスレッドに送るデータ頻度は1/100秒以上にしてみてください。ちゃんと処理するには、データ処理部分もスレッド化する必要が出てきます。

ここまで原点に戻れば自ずと問題箇所はみえてくると思います。  
恐らくはスレッドの理解が足りないのだと思います。


KHE00221  2011-03-11 09:56:53  No: 40231

TForm2Thread = class(TForm)
 TForm2 = class(TThread)

名前逆じゃないか・・・


KHE00221  2011-03-11 10:08:22  No: 40232

Form2: TForm2 があってどっかで
誤動作してるんじゃないのか?


とじ  2011-03-11 19:09:08  No: 40233

monaa様、
ありがとうございます。
最小構成で作り直し、、、というか移植し直しを考えます。
Delphi7でビルドしてこの現象は出ていないと言う事なので、
Delphi2007でビルド出来るようになって出たのだと思います。

小出しで申し訳ないのですが、OSが同じXPでも現象が出ないPCもあり、ハードが古めのPCで出る様な気がします。で、実際にビルドしているPC(Vista SP2)では現象が出ません。
メモリ漏れはないと思うのですが、原因はまだ不明です。

KHE00221様、
ありがとうございます。
名前が紛らわしいですが、逆なんです。ワーカスレッド作成でも

Form2: TForm2;
..
..
Form2 := TForm2.Create(True);

と書いてあるところはなく、Unit1.pasで

Thread1: TForm2;
..
..
Thread1 := TForm2.Create(True);

としています。

TForm2スレッドが定義してあるUnit2.pasで、
TForm2Threadフォームも定義してあり、
TForm2スレッドの受信処理で、TForm2Threadフォームの
メソッドやプロパティを使用しています。

monaa様のご指摘の「(ワーカ)スレッドでは行ってはいけない動作...メインフォームのVCLにアクセスしたりしてはいけません」を見て気になりところもあります。
TForm2スレッドの受信処理メソッドでForm1フォームのプロパティを操作したり、同フォームのメソッドを呼んだりしているのですが、これも問題あるかもしれません。
例えば、Form1.CommErr := True;とかForm1.StopProcess;など、、、


TS  2011-03-11 23:14:36  No: 40234

名は体を現すと言いますが。
>名前が紛らわしいですが、逆なんです。ワーカスレッド作成でも
紛らわしいことは止めましょう。
Thread1: TForm2;  は  女:T男  と書いている様な物です。
スレッドとフォームは別物として処理しましょう。

過去の記事の検索でスレッド,Synchronizeでまず検索をしてみて下さい。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加