16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル
"RE:1つのスレッドを何度も使いまわす"
この発言は #00971 Satobe さんの1つのスレッドを何度も使いまわす に対するコメントです
前回のサンプルでは、スレッド実行中に Startメソッドを呼び出
した場合に、State := 0;という初期状態から再開してくれる保証
がありませんでした。
そこで、Execute内部で Suspended:=True;が実行されるまで待つ
WaitForInternalSuspendというメソッドを追加してみました。
Startメソッドに渡した SL:TStringsを途中で Freeした場合、ス
レッドが動作中であるとスレッド側の処理で例外が発生してしま
う可能性がありますが、このような場合にも前もって
WaitForInternalSuspendメソッドを呼び出してから Freeすれば
問題ありません。
----- ここから -----
type
TMyThread = class(TThread)
private
FRestartFlag: Boolean;
FInternalSuspendFlag: Boolean;
FStartNo: Integer;
FSL: TStrings;
FAddStr: String;
procedure Clear;
procedure Add;
public
constructor Create;
procedure Execute; override;
procedure WaitForInternalSuspend(ForceSuspend: Boolean);
procedure Start(SL: TStrings; StartNo: Integer);
end;
TForm1 = class(TForm)
..省略
private
FMyThread: TMyThread;
..省略
implementation
constructor TMyThread.Create;
begin
inherited Create(True); //Suspend状態で生成する
FRestartFlag := False;
FInternalSuspendFlag := False;
FStartNo := 0;
FSL := nil;
//FreeOnTerminate := True;
end;
procedure TMyThread.Execute;
var
State, No, EndNo: Integer;
begin
while not Terminated do begin
//例外発生で終わらないように
try
FRestartFlag := False;
State := 0;
while (not Terminated) and
(not FRestartFlag) do begin
case State of
0:begin
Synchronize( Clear ); //FSLをクリア
No := FStartNo;
EndNo := No + 9;
inc(State);
end;
1:begin
FAddStr := IntToStr(No);
Synchronize( Add );
Sleep(500); //※for DEBUG
inc(No);
if No > EndNo then State := -1; //完了した
end;
else //完了したらSuspend
Suspended := True;
end;
if FInternalSuspendFlag then begin
//速やかに Suspend状態に移行
State := -1;
end;
end;
except
on E:Exception do begin
if not(E is EAbort) then Application.ShowException(E);
if E is EThread then Exit;
Suspended := True;
end;
end;
end;
end;
procedure TMyThread.WaitForInternalSuspend(ForceSuspend: Boolean);
var
CurrentThreadID: THandle;
Msg: TMsg;
begin
//Execute内部で Suspended := Trueが実行されるまで待つ
//ForceSuspendパラメータ
// True: 実行中の処理を中断して速やかに Suspend状態にする
// False: 実行中の処理が最後まで完了するのを待つ
//
if Suspended then Exit; //すでに Suspend状態
//念のため
CurrentThreadID := GetCurrentThreadID;
if CurrentThreadID = ThreadID then
raise Exception.Create('同一スレッド内部から'
+'WaitForInternalSuspendが呼び出されました');
FInternalSuspendFlag := ForceSuspend;
try
while not Suspended do begin
//自分がメインスレッドならば、
//スレッドからのSynchronizeを受け付けさせる
if CurrentThreadID = MainThreadID then
PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
end;
finally
FInternalSuspendFlag := False;
end;
end;
procedure TMyThread.Start(SL: TStrings; StartNo: Integer);
begin
WaitForInternalSuspend(True); //一時停止
//リスタート情報設定
FRestartFlag := True;
FStartNo := StartNo;
FSL := SL;
Suspended := False; //スレッド動作再開
end;
procedure TMyThread.Clear;
begin
if Assigned(FSL) then FSL.Clear;
end;
procedure TMyThread.Add;
begin
if Assigned(FSL) then FSL.Add(FAddStr);
end;
//スレッドオブジェクトの生成&破棄
procedure TForm1.FormCreate(Sender: TObject);
begin
FMyThread := TMyThread.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(FMyThread) then begin
FMyThread.Terminate;
while FMyThread.Suspended do
FMyThread.Suspended := False;
FMyThread.Free;
end;
end;
//スレッド動作開始
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(FMyThread) then begin
FMyThread.Start(Memo1.Lines,
StrToIntDef(Edit1.Text, 0));
end;
end;
----- ここまで -----
上記 TMyThreadクラス内部の FInternalSuspendFlag変数は、ス
レッド外部から True/Flaseを書き込み、スレッド内部では参照
のみ、という扱いになっているため、クリティカルセクションな
どを使った排他制御は行っておりません。
このクラスを改造して、複数スレッドから書き込みが行われるよ
うな処理を行うならば、排他制御が必要になることをお忘れなく。
#「排他制御」ではなく「スレッド間の同期」というのが正しい
#用語なのかしらん?
99/11/06(土) 23:35 Satobe(JCG00336)
Original document by Satobe 氏 ID:(JCG00336)
ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。
Copyright 1996-2002 Delphi Users' Forum
|