[Delphi:91117] スレッド終了直後のString操作でエラーが出ることがある

826 views
Skip to first unread message

飯塚 伸二(ShinjiIizuka)

unread,
Feb 23, 2009, 1:03:04 AM2/23/09
to Del...@ml.users.gr.jp
飯塚@HMCです。

メインスレッドからワーカスレッドを開始し、
ワーカースレッドの終了を待ってからメインスレッドで
String型文字列操作を行うとエラーが出る現象に遭遇しました。

エラーの内容は、以下の通りです。
Exception EAccessViolation in module test.exe at 000036CC.
Access violation at address 004036CC in module 'test.exe'. Read of address 41414141.

エラーが出ないように修正はできるのですが、エラー原因が分からないので、
本当に直っているのかが不安です。
原因をご存知の方、何か情報をお持ちの方、お助け願います。

エラーが出る環境:
Windows XP Professional SP1
Pentium4 2.4GHz
2GB RAM
Delphi6 , Delphi3.1
上記環境で、90%近くエラーが再現します。
他のパソコンではエラーが出なかったりします。

以下に、エラーが出るソースを載せます。
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;

var
Form1: TForm1;

procedure Th_Exec;

implementation

{$R *.dfm}

type
TThreadFunc = class(TThread)
private
{@1}a: array [0..4096] of Integer;
procedure ThreadFuncEndProc(Sender: TObject);
protected
procedure Execute; override;
public
end;

var
ThreadFuncTerminated: Boolean; //スレッド終了感知フラグ

//スレッド終了時に呼び出されるイベントハンドラ
procedure TThreadFunc.ThreadFuncEndProc(Sender: TObject);
begin
ThreadFuncTerminated := True; //スレッド終了感知フラグTrue
end;

//スレッド実行部
procedure TThreadFunc.Execute;
begin
Sleep(2000);
end;

procedure Th_Exec;
var
ThreadFunc: TThreadFunc;
begin
ThreadFunc := TThreadFunc.Create(True);
{@2} ThreadFunc.FreeOnTerminate := False;
ThreadFunc.OnTerminate := ThreadFunc.ThreadFuncEndProc;
ThreadFuncTerminated := False;
ThreadFunc.Resume;

//スレッドが終了するまで待つ
repeat
Sleep(10);
Application.ProcessMessages;
until ThreadFuncTerminated;

{@3} ThreadFunc.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
S: String;
i: Integer;
begin
try
Th_Exec;
{@4}
S := 'A';
for i:=0 to 40 do
S := S +'A';
MessageDlg(S, mtInformation, [mbOk], 0);
except
MessageDlg('例外捕捉', mtError, [mbOk], 0);
end;
end;

end.

【参考事項】
・[Button1]をクリックすると、'AAAAA'のダイアログとともに、エラーが出る。
・{@2}のFreeOnTerminateをTrueにし、{@3}のFreeを削除すると、エラーが出なくなる。
・{@1}の使っていない領域を削除すると、エラーが出なくなる。
・{@3}の辺りに Sleep(1) を入れると、エラーが出なくなる。
・{@4}の辺りで、String型文字列操作をしなければ、エラーは出ない。
・エラーのRead of address 41414141 は、{@4}の AAAA のところ?
・エラー時、except で例外を捕捉できません。

#長文ですみません
-----------------------------------------------
飯塚 伸二
iizuka...@kf.hitachi-medical.co.jp
-----------------------------------------------

Tomomi Umezawa

unread,
Feb 23, 2009, 3:09:08 AM2/23/09
to Del...@ml.users.gr.jp
こんにちは、梅澤@プロキャストです。

WaitForを使って、終了を待ってみてはどうでしょう?

----------
Tomomi Umezawa um...@procast.co.jp


飯塚 伸二(ShinjiIizuka)

unread,
Feb 23, 2009, 3:17:01 AM2/23/09
to Del...@ml.users.gr.jp
飯塚@HMCです。

>WaitForを使って、終了を待ってみてはどうでしょう?
WaitForを使うと、メインスレッドが終了を待っている間、
イベントに反応できなくなってしまうので
Application.ProcessMessagesをループで回しています。

なお、エラーが出ないようにする対策方法ではなく、
エラーが発生する原因を知りたいのです。
よろしくお願いします。

-----------------------------------------------
飯塚 伸二
iizuka...@kf.hitachi-medical.co.jp
-----------------------------------------------

Tomomi Umezawa

unread,
Feb 23, 2009, 3:52:40 AM2/23/09
to Del...@ml.users.gr.jp
こんにちは、梅澤@プロキャストです。

説明不足ですみません。

ループはいままで通り、Application.ProcessMessagesを使っておき、
until の後、ThreadFunc.Freeをする前に、WaitForしてみてはどうで
しょう?
という意味でした。

エラーの原因の予想としては、スレッド側インスタンスが完全に処理
を終えていないのに、Freeしてしまっているのでは?
と思っています。
(OnTerminate イベントの直後では、まだサブスレッド側が動いている
のに、Freeしようとしている)


飯塚 伸二 さんは書きました。< Mon, 23 Feb 2009 17:17:01 +0900 >

----------
Tomomi Umezawa um...@procast.co.jp


TAKAHASHI, Tomohiro

unread,
Feb 23, 2009, 4:43:45 AM2/23/09
to Del...@ml.users.gr.jp
高橋(智)です。

試しに、プログラムの初期化処理として
IsMultiThread := True;
の一行をどこかに追加してみてください。
スレッドオブジェクトを新規作成した場合など、メモリマネージャがマルチスレッド
対応である必要がある場合には、自動的に IsMultiThread := True; になるばすですが...
Delphi6などでは、IsMultiThread := True; にならないこともあるようですね

--
高橋智宏

aaa1...@pop06.odn.ne.jp

unread,
Feb 23, 2009, 5:08:40 AM2/23/09
to Del...@ml.users.gr.jp
 こんにちは、イマジオムの高木です。

飯塚さん:

 これ、まずいです。 上記のコードの中に問題があります。

 ワーカスレッドの終了時には OnTerminate イベントが発生しますが、
ワーカスレッドはこのイベントハンドラを呼び出した後、何もしないわけでは
ありません。 少なくとも関数呼び出しの巻き戻し(スタックからのポップ)を
したり、ワーカスレッドの使っていたリソースを返したり、いろいろなことを
しなければなりません。 それにもかかわらず、メインスレッドが {@3} で
ThreadFunc を解放してしまったので、ワーカスレッドがエラーを出したのです。

 WaitFor を使うか、FreeOnTerminate を True にするのが正しい解決策です。
――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎
〒316-0024 茨城県 日立市 水木町 1-11-10
電話:0294-28-0147
ファクシミリ:0294-28-0148
電子メール:tarou_...@imageom.co.jp
ホームページ:http://www.imageom.co.jp/

Jun'ya Shimoda

unread,
Feb 23, 2009, 10:09:31 AM2/23/09
to Del...@ml.users.gr.jp
下田と申します。

昨夜奇遇にも私もマルチスレッドアプリを作っていたので
分かる範囲で返事いたします。

解決法は何種類かありますが、ソースをいじらない方から解決策を
挙げますと

1.{@3}の部分を
  ThreadFunc.DoTerminate;
  としてみてください。
  エラーの原因は「スレッドを正しく終了させてないのに
  Freeしているから」という可能性が非常に高いです。

2.{@2}の部分を
  ThreadFunc.FreeOnTerminate := True;
  として、{@3}をコメントアウトしてしまう。
  どうせ処理が終われば開放するのですから
  1.より更にマシだと思われます。

3.以上の解だとスレッドが終わるまで処理を待つので
  正直、マルチスレッドにする意味がないです。
  Th_Execは
//スレッド実行部


procedure Th_Exec;
var
ThreadFunc: TThreadFunc;
begin

ThreadFunc := TThreadFunc.Create(False);
{@2} ThreadFunc.FreeOnTerminate := True;
ThreadFunc.OnTerminate := ThreadFunc.ThreadFuncEndProc;
end;
  とするとスレッド実行中もボタンイベントを受け付けます。
  試しに連打してみてください。
  スレッドが終わったかどうか確認したいなら
  ThreadFuncEndProcを


//スレッド終了時に呼び出されるイベントハンドラ
procedure TThreadFunc.ThreadFuncEndProc(Sender: TObject);
begin

Application.MessageBox('スレッド終了', nil);
end;
  とすると、メッセージボックスが出て確認できます。

4.どうしてもスレッド終了まで同期がとりたいなら
  上記のTh_Exec処理の最後に
ThreadFunc.Synchronize(ThreadFunc.Execute);
  を入れるとExecute処理終了まで待ちます。

マルチスレッドアプリを作っていて
sleepとかwhileとかuntilなどの
待つ処理やループを使いたくなったときは
何か設計を間違っている可能性が高いです。

下記が参考になるかと思います。
http://support.codegear.com/jp/article/35961

では、頑張ってください。


飯塚 伸二(ShinjiIizuka) さんは書きました:

飯塚 伸二(ShinjiIizuka)

unread,
Feb 23, 2009, 7:08:45 PM2/23/09
to Del...@ml.users.gr.jp
飯塚@HMCです。

エラー原因は、ワーカスレッドが終了していないのにFree を呼び出して
いたことによって、ワーカスレッド側でエラーとなっていたのですね。
理解いたしました。

梅澤@プロキャスト さん
高橋(智) さん
イマジオムの高木 さん
下田 さん
本当に有難うございました。

Reply all
Reply to author
Forward
0 new messages