# <1> スレッドオブジェクト (Delphi コンカレントプログラミング) --- tags: Delphi プログラミング Pascal embarcadero objectpascal created_at: 2021-12-04 updated_at: 2024-10-29 --- # 1. スレッドオブジェクト `TThread` クラスは、アプリケーション内で別の実行スレッド (バックグラウンドスレッド) を作成できるようにする抽象クラスで、`System.Classes` で定義されています。`BeginThread()` と `EndThread()` が内部で使われています。 - [System.Classes.TThread (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread) 最初期のこのクラスは Windows API をラップしたものでした。 | Delphi | Windows API | |:---|:---| | [TThread.Create()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.Create) コンストラクタ | [System.BeginThread()](https://docwiki.embarcadero.com/Libraries/ja/System.BeginThread) = [CreateThread()](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread) | | [TThread.Priority](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.Priority) プロパティ| [GetThreadPriority()](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadpriority) / [SetThreadPriority()](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadpriority) | | [TThread.Resume()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.Resume) メソッド | [ResumeThread()](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-resumethread) | | [TThread.Suspend()](https://docwiki.embarcadero.com/Libraries/Sydney/ja/System.Classes.TThread.Suspend) メソッド | [SuspendThread()](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-suspendthread) | 最近の Delphi の `TThread` クラスはマルチプラットフォーム対応ですが、`Resume()` / `Suspend()` メソッドは非推奨とマークされている [^1] 上に、Windows / macOS プラットフォーム向けにしか実装されていません。 `BeginThread()` を実行するか、スレッドオブジェクトが生成されると、**IsMultiThread** グローバル変数が True になり、メモリマネージャがマルチスレッドをサポートするようになります。Windows API を直接叩いた場合には IsMultiThread を手動で True に変更する必要があります。 ## 1.1. TThread の使い方 `TThread` は抽象クラスなので、派生して (サブクラス化して) 使います。 ### 1.1.1. TThread の派生 スレッドオブジェクトを作るためのウィザードは `[ファイル | 新規作成 | その他]` から呼び出せます。 ![image.png](./images/0b5e33c5-6dcc-f6b1-2ece-29edab06b8b3.png) スレッドクラス名を `TMyThread` にして、 ![image.png](./images/8a52127c-5cda-12fc-cd86-622f7681256a.png) 自動生成されたユニットが次のようなコードになります。 ```pascal:unit2.pas unit Unit2; interface uses System.Classes; type TMyThread = class(TThread) private { Private 宣言 } protected procedure Execute; override; end; implementation { TMyThread } procedure TMyThread.Execute; begin { スレッドとして実行したいコードをここに記述してください } end; end. ``` スレッドとして実行したいコードは `Exceute()` メソッドに記述します。 慣れてくると、ウィザードを使わずにスレッドクラスを書けるようになりますが、`〔Shift〕+〔Ctrl〕+〔C〕`でクラス補完すると `TThread.Execute()` メソッドに **inherited** が追加されてしまいます。`TThread.Execute()` は抽象メソッドなので、**inherited** の記述は不要です。 ```pascal procedure TMyThread.Execute; begin inherited; // 不要 end; ``` **See also:** - [スレッドオブジェクトの定義 (DocWiki)](https://docwiki.embarcadero.com/RADStudio/ja/%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89_%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E5%AE%9A%E7%BE%A9) ### 1.1.2. スレッドオブジェクトの実行 スレッドクラスはインスタンス化したと同時に実行されます (Execute メソッドが呼ばれます)。 ```pascal constructor Create; overload; constructor Create(CreateSuspended: Boolean); overload; {$IF Defined(MSWINDOWS)} constructor Create(CreateSuspended: Boolean; ReservedStackSize: NativeUInt); overload; {$ENDIF MSWINDOWS} ``` - 最初の書式 [^2] はインスタンス化したと同時に Execute メソッドが呼ばれます。 - 二番目の書式は `CreateSuspended` パラメータを True に設定する事により、スレッドを中断した状態 (サスペンド状態) で作成します。Execute メソッドは `Start()` [^3] (または `Resume()`) メソッドが呼ばれるまで実行されません。 - 三番目の書式 [^4] は `ReservedStackSize` を指定する事でスタックサイズを設定できますが、Windows 専用です。 `Execute()` メソッドを抜けると、スレッドが終了してしまうので、繰り返し処理したい場合には `Terminated` プロパティをチェックしながらループします。 ```pascal procedure TMyThread.Execute; begin while not Terminated do begin ... end; end; ``` :::note warn 古い Delphi ではサスペンド状態で作成したスレッドオブジェクトを Resume() することなく破棄するとメモリリークを起こしていました。 ::: :::note warn Resume() メソッドはサスペンド状態で作成されたスレッドを開始するためにのみ使うべきです。また、この用途のための Resume() はマルチプラットフォーム対応となっています。 ::: **See also:** - [System.Classes.TThread.Create (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.Create) - [System.Classes.TThread.Start (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.Start) ### 1.1.3. FreeOnTerminate スレッド実行完了時に自動でスレッドオブジェクトを破棄したい場合には、`FreeOnTerminate` プロパティを True に設定します。 ```pascal MyThread := TMyThread.Create(True); MyThread.FreeOnTerminate := True; MyThread.Start; ``` `FreeOnTerminate` プロパティを True に設定するタイミングは `Execute()` メソッドが終了する前であればどこでも構わないため、スレッドをサスペンド状態で作成したくないのなら、`Execute()` メソッドの先頭で記述する事もできます。 ```pascal procedure TMyThread.Execute; begin FreeOnTerminate := True; ... end; ``` スレッドオブジェクトは実行後に自動破棄されますが、スレッド実行中にアプリケーションを終了させた場合には普通にメモリリークします。 ![image.png](./images/f8db7ac6-250c-a4ee-f8f0-d3cf2fd71a11.png) スレッドが実行中の場合には適切な中止処理を行う必要があります。 **See also:** - [System.Classes.TThread.FreeOnTerminate (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.FreeOnTerminate) ### 1.1.4. OnTerminate イベント `TThread` にはスレッドが終了した時に呼ばれる `OnTerminate` イベント (`TNotifyEvent` 型) があります。 ```pascal MyThread := TMyThread.Create(True); MyThread.FreeOnTerminate := True; MyThread.OnTerminate := MyThreadTerminate; MyThread.Start; ``` `OnTerminate` のイベントハンドラはメインスレッドで実行されます。 **See also:** - [System.Classes.TThread.OnTerminate (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.OnTerminate) ### 1.1.5. 明示的なスレッドオブジェクトの破棄 `FreeOnTerminate = False` なスレッドオブジェクトを明示的に破棄するには `FreeAndNil()` を使います。 ```pascal FreeAndNil(MyThread); ``` 丁寧な破棄は次のようになります。 ```pascal MyThread.Terminate; MyThread.WaitFor; FreeAndNil(MyThread); ``` 結局の所、いきなり `FreeAndNil()` しても上記処理が実行されます。 :::note info MyThread.Free で破棄するよりも、FreeAndNil() を使って破棄した方がトラブルは少なくなると思います。 ::: ### 1.1.6. スレッドオブジェクトの終了 (中止) #### ・FreeOnTerminate = True なスレッドオブジェクトの終了 (中止) `Terminate` メソッドを呼びます。 ```pascal if Assigned(MyThread) then MyThread.Terminate; ``` この `Terminate` メソッドは、基本的に `Terminated` プロパティを True に設定しているだけなので、`Execute()` メソッドには (`Terminated` プロパティをチェックしながら) スレッドを正しく終わらせられるコードを記述する必要があります。 #### ・FreeOnTerminate = False なスレッドオブジェクトの終了 (中止) `FreeAndNil()` で破棄するのが簡単だと思います。 ```pascal FreeAndNil(MyThread); ``` ### 1.1.7. スレッドにパラメータを渡す 普通のクラスと同じようにコンストラクタを定義します。 ```pascal public { public 宣言 } constructor Create(Index: Integer); ... constructor TMyThread.Create(Index: Integer); begin inherited Create(False); Self.FreeOnTerminate := True; // 処理 end; ``` ### 1.1.8. スレッドに名前を付ける デバッグ用途でスレッドに名前を付けたい場合には、クラスメソッド `NameThreadForDebugging()` を使います [^5]。ウィザードで `名前付きスレッド` にチェックを入れると同等のコードが吐かれます [^6]。 ```pascal procedure TMyThread.Execute; begin NameThreadForDebugging('MyThread1'); while not Terminated do begin ... end; end; ``` 古い Delphi のウィザードでは `NameThreadForDebugging()` と同等の `SetName()` メソッドが自動生成されていました。 ```pascal unit Unit2; interface uses Classes {$IFDEF MSWINDOWS} , Windows {$ENDIF}; type TMyThread = class(TThread) private procedure SetName; protected procedure Execute; override; end; implementation {$IFDEF MSWINDOWS} type TThreadNameInfo = record FType: LongWord; // 0x1000 にすること。 FName: PChar; // (ユーザー空間での) 名前文字列へのポインタ FThreadID: LongWord; // スレッド ID (呼び出しスレッドは -1) FFlags: LongWord; // 将来のために予約。0 にすること。 end; {$ENDIF} { TMyThread } procedure TMyThread.SetName; {$IFDEF MSWINDOWS} var ThreadNameInfo: TThreadNameInfo; {$ENDIF} begin {$IFDEF MSWINDOWS} ThreadNameInfo.FType := $1000; ThreadNameInfo.FName := 'MyThread'; ThreadNameInfo.FThreadID := $FFFFFFFF; ThreadNameInfo.FFlags := 0; try RaiseException( $406D1388, 0, sizeof(ThreadNameInfo) div sizeof(LongWord), @ThreadNameInfo ); except end; {$ENDIF} end; procedure TMyThread.Execute; begin SetName; { ToDo : スレッドとして実行したいコードをこの下に記述してください } end; end. ``` **See also:** - [スレッドに名前を付ける (DocWiki)](https://docwiki.embarcadero.com/RADStudio/ja/%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%AB%E5%90%8D%E5%89%8D%E3%82%92%E4%BB%98%E3%81%91%E3%82%8B) - [System.Classes.TThread.NameThreadForDebugging (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.NameThreadForDebugging) ### 1.1.9. Synchronize() メソッド VCL / FMX コンポーネントはメインスレッドで動いているので、バックグラウンドスレッドからコンポーネントを更新したい場合には、`Synchronize()` メソッドを使い、パラメータで指定された手続きをメインスレッド内で実行します。 ```pascal procedure TMyThread.Hoge; begin // 何かしらの VCL / FMX コンポーネントを操作する処理 end; procedure TMyThread.Execute; begin NameThreadForDebugging('MyThread1'); while not Terminated do begin ... Synchronize(Hoge); ... end; end; ``` 指定した手続きがメインスレッド内で実行されている間はバックグラウンドスレッドは停止していますので、頻繁に呼び出すべきではありません。また、`Synchronize()` はパラメータとして - 手続きのメソッドポインタ型 (**of object**) - 手続きのメソッド参照型 (**reference to**) を受け付けるため、ここに**無名メソッド**を指定する事ができます [^7]。 ```pascal procedure TMyThread.Execute; begin NameThreadForDebugging('MyThread1'); while not Terminated do begin ... Synchronize( procedure begin // 何かしらの VCL / FMX コンポーネントを操作する処理 end ); ... end; end; ``` :::note warn Synchronize() はメッセージループを利用するため、コンソールアプリケーションでは使用できません。 ::: :::note warn Delphi 6 以降、Synchronize() は DLL 内で機能しなくなりました。 ::: **See also:** - [System.Classes.TThread.Synchronize (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.Synchronize) - [Delphi での無名メソッド (DocWiki)](https://docwiki.embarcadero.com/RADStudio/ja/Delphi_%E3%81%A7%E3%81%AE%E7%84%A1%E5%90%8D%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89) - [D6DLLSynchronizer for Delphi 6 and 7 (Code Central)](https://cc.embarcadero.com/item/21148) ### 1.1.10. CreateAnonymousThread() メソッド `TThread` クラスには `CreateAnonymousThread()` というクラスメソッドが用意されています [^8]。手続きのメソッド参照型をパラメータとして受け付けるこのメソッドは、内部的に生成された TThread 派生クラスのインスタンスを返します。スレッドオブジェクトは - サスペンド状態 - FreeOnTerminate = True で作られるため、次のような使い方になります。 ```pascal TThread.CreateAnonymousThread( procedure begin // 何かしらの処理 end ).Start; ``` `Synchronize()` はどうするのかと言うと、クラスメソッドの方の `Synchronize()` を使います [^9]。 ```pascal TThread.CreateAnonymousThread( procedure begin // 何かしらの処理 TThread.Synchronize(TThread.CurrentThread, procedure begin // 何かしらの VCL / FMX コンポーネントを操作する処理 end); end ).Start; ``` `OnTerminate` はどうするのかと言うと、一旦変数に取ります。 ```pascal procedure TForm1.MyThreadTerminate(Sender: TObject); begin // 何かしらのスレッド終了時処理 end; procedure TForm1.FormCreate(Sender: TObject); begin var MyThread := TThread.CreateAnonymousThread( procedure begin // 何かしらの処理 TThread.Synchronize(TThread.CurrentThread, procedure begin // 何かしらの VCL / FMX コンポーネントを操作する処理 end); end ); MyThread.OnTerminate := MyThreadTerminate; MyThread.Start; end; ``` こうなってしまうと、普通に派生クラス (サブクラス) を書いた方が可読性が高そうです。 **See also:** - [System.Classes.TThread.CreateAnonymousThread (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.CreateAnonymousThread) - [引数を渡せる匿名スレッド (Qiita: @MiraiNagato)](https://qiita.com/MiraiNagato/items/7207d5456dbb83b25272) ### 1.1.11. Queue() メソッド `Synchronize()` を `Queue()` [^10] で置き換えると、バックグラウンドスレッドがパラメータで指定された手続きの実行を待たなくなります。 ただし、`Queue()` に指定した手続きの呼び出しは、スレッドが終了すると削除されてしまうので注意が必要です。新規に VCL アプリケーションを作って確認してみます。フォームにはメモとボタンが一つずつあります。 ![image.png](./images/f25fc1db-a448-5148-d69b-4e654c10758a.png) ボタンのイベントハンドラに無名スレッドを記述します。 ```pascal procedure TForm1.Button1Click(Sender: TObject); begin TThread.CreateAnonymousThread( procedure begin TThread.Queue(TThread.Current, procedure begin Memo1.Lines.Clear; Memo1.Lines.Add('start'); end); TThread.Sleep(2000); TThread.Queue(TThread.Current, procedure begin Memo1.Lines.Add('end'); end); end ).Start; end; ``` ボタンを押すとメモに `start` が表示され、2 秒後に `end` が表示される...ハズですが、メモにはいつまで経っても `start` だけが表示されていると思います。 ![image.png](./images/9d3426d4-aa83-3ada-75b8-b3a51ba355fe.png) ドキュメントには次のように書かれています。 > メイン スレッドは、最終的にキューに入っているすべてのメソッドを処理します。 嘘ではないですが、それはスレッドが終了していない場合の話です。つまり、先にスレッドが終了してしまったので、それ以降のメソッド呼び出しが削除 (キャンセル) されてしまったのです。 #### ・解決方法 1 `Queue()` の最初のパラメータに **nil** を指定します。 ```pascal procedure TForm1.Button1Click(Sender: TObject); begin TThread.CreateAnonymousThread( procedure begin TThread.Queue(TThread.Current, procedure begin Memo1.Lines.Clear; Memo1.Lines.Add('start'); end); TThread.Sleep(2000); TThread.Queue(nil, // <- nil を指定 (最後の一つだけで構わない) procedure begin Memo1.Lines.Add('end'); end); end ).Start; end; ``` `TThread.Queue()` の最初のパラメータが **nil** でない場合、キューに入れられた呼び出しがパラメータで指定されたスレッドと関連付けられ、スレッドが終了すると、まだ処理されていない呼び出しは `TThread.RemoveQueuedEvents()` によりキャンセルされます。 #### ・解決方法 2 ダミーの `Synchronize()` をキューの最後に追加します。 ```pascal procedure TForm1.Button1Click(Sender: TObject); begin TThread.CreateAnonymousThread( procedure begin TThread.Queue(TThread.Current, procedure begin Memo1.Lines.Clear; Memo1.Lines.Add('start'); end); TThread.Sleep(2000); TThread.Queue(TThread.Current, procedure begin Memo1.Lines.Add('end'); end); TThread.Synchronize(TThread.Current, procedure begin end); // <- ダミーのSynchronize() を追加 end ).Start; end; ``` `Synchronize()` がキューの最後に追加されると待ちが発生するため、結果的に `Queue()` で追加された呼び出しはすべて処理される事になります。 ![image.png](./images/180f4f60-ecb3-5159-436e-d7a89068574e.png) :::note warn Queue() はメッセージループを利用するため、コンソールアプリケーションでは使用できません。 ::: **See also:** - [System.Classes.TThread.Queue (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.Queue) - [Delphi アルゴリズムトレーニング 第2回 単純なキューと循環キュー (@IT)](https://atmarkit.itmedia.co.jp/fcoding/articles/delphi/02/delphi02a.html) - [Delphi Queue and Synchronize (StackOverflow)](https://stackoverflow.com/questions/42280937/delphi-queue-and-synchronize) ### 1.1.12. Sleep() クラスメソッド `TThread.Sleep()` [^11] は指定したミリ秒数だけプログラム実行を停止させます。 現在の実装では `System.Classes.TThread.Sleep()` メソッドと `System.SysUtils.Sleep()` 手続きは同じ処理内容になっているため、単に `Sleep()` と書いても (どちらが使われても) 問題ありません。将来、何かしらの変更が発生する事も考慮して、記事中では `TThread.Sleep()` を使っています。 この **Sleep** を (別のスレッドから) 途中で終了させるメソッドも現在の所では用意されていません。 **See also:** - [System.SysUtils.Sleep (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SysUtils.Sleep) - [System.Classes.TThread.Sleep (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.Sleep) ### 1.1.13. SpinWait() クラスメソッド `TThread.SpinWait()` [^12] は指定したスピンループ分だけプログラム実行を遅延させます。 空の **for** ループを回して時間調整するようなものです。 **See also:** - [System.Classes.TThread.SpinWait (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.SpinWait) ### 1.1.14. WaitFor() メソッド `WaitFor()` メソッドを呼び出すとスレッドが終了するまで待ちます。 `WaitFor()` メソッドが返す値は `ReturnValue` プロパティの値です。`ReturnValue` はスレッドにおける Result 変数のようなものです。 ```pascal procedure TForm1.Button1Click(Sender: TObject); var MyThread: TMyThread; rv: Integer; begin MyThread := TMyThread.Create; rv := MyThread.WaitFor; // スレッドが終了するのを待つ FreeAndNil(MyThread); ShowMessage(IntToStr(rv)); end; ``` - `WaitFor()` メソッドは `FreeOnTerminate` プロパティが True の時には使えません。 - `WaitFor()` メソッドはスレッドが終了するまで、呼び出し元のスレッドをブロックします。つまりメインスレッドで実行された場合、`WaitFor()` で待つ間はフォームを動かせなくなります。 **See also:** - [System.Classes.TThread.WaitFor (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.WaitFor) - [System.Classes.TThread.ReturnValue (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.ReturnValue) ### 1.1.15. コンソールアプリケーションでの Synchronize / Queue 先述の通り、コンソールアプリケーションではメッセージループがないため、コンソールアプリケーションでは `Synchronize()` / `Queue()` が動作しません。 次のコンソールアプリケーションを実行しても、何も表示されません (中断は〔Ctrl〕+〔C〕)。 ```pascal:ApplePen.dpr program ApplePen; {$APPTYPE CONSOLE} uses System.Classes, System.Threading; type TMyThread = class(TThread) private FData: string; procedure MyThreadTerminate(Sender: TObject); protected procedure Execute; override; public constructor Create(s: String); end; var TermCount: Integer; constructor TMyThread.Create(s: String); begin inherited Create(True); FreeOnTerminate := True; OnTerminate := MyThreadTerminate; FData := s; end; procedure TMyThread.Execute; begin Synchronize(procedure begin Writeln(FData) end); end; procedure TMyThread.MyThreadTerminate(Sender: TObject); begin Inc(TermCount); end; begin TermCount := 0; TMyThread.Create('I have a pen.').Start; TMyThread.Create('I have an apple.').Start; TMyThread.Create('uh...Apple Pen.').Start; while TermCount < 3 do ; end. ``` 最後のループに `CheckSynchronize()` [^13] を追加すると正しく動作するようになります。 ```pascal while TermCount < 3 do CheckSynchronize; ``` **See also:** - [System.Classes.CheckSynchronize (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.CheckSynchronize) ## 1.2. TThread のデモ TThread のデモは、最近の Delphi には含まれていないようです [^14]。古い Delphi だと `$(DELPHI)\DEMOS\Threads` や `Delphi\RTL\Threads`、`Object Pascal\RTL\Threads` 等にあります。 この `Threads` デモは XE ~ XE6 のサンプルリポジトリに残っています。 - [XE6 のサンプルリポジトリ (SourceForge)](https://svn.code.sf.net/p/radstudiodemos/code/branches/RadStudio_XE6/) サンプルリポジトリから `Threads` デモを開くには `[ファイル | バージョン管理リポジトリから開く...]` を選択し、バージョン管理システムとして `Subversion` を選びます。 ![image.png](./images/9d9b8e08-33a4-4c76-6406-f7123fc8f0a7.png) リポジトリの場所とコピー先には次のように指定します。 | 項目 | 値 | |:---|:---| | リポジトリの場所 | [https://svn.code.sf.net/p/radstudiodemos/code/branches/RadStudio_XE6/Object Pascal/RTL/Threads/](https://svn.code.sf.net/p/radstudiodemos/code/branches/RadStudio_XE6/Object%20Pascal/RTL/Threads/) | | コピー先 | 既存の任意のフォルダ (ここでは C:\Work\Threads) | ![image.png](./images/c37e5944-6434-fc29-5790-577f2a0e6787.png) [OK] ボタンを押すとダウンロードが開始され、ダウンロードが終わるとプロジェクトを選択するダイアログが開くので、`thrddemo.dpr` を選択します。 ![image.png](./images/257d15ca-4b51-79c4-9aa9-f0e28b864ff6.png) ユニット `SortThds.pas` の **uses** に `Types` を追加すると、ヒントやワーニングなしでコンパイルできます。 ```pascal:SortThds.pas unit SortThds; interface uses Classes, Types, Graphics, ExtCtrls; ... ``` ![image.png](./images/05f00624-1a92-24c2-83c2-3ef3555b1767.png) ![image.png](./images/9f03c949-3834-0d72-2f1a-efbc91e59c98.png) **See also:** - [Synchronize (Delphi) (DocWiki)](https://docwiki.embarcadero.com/CodeExamples/en/Synchronize_(Delphi)) - [デバッガでのスレッドの凍結および凍結解除 (DocWiki)](https://docwiki.embarcadero.com/RADStudio/ja/%E3%83%87%E3%83%90%E3%83%83%E3%82%AC%E3%81%A7%E3%81%AE%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%AE%E5%87%8D%E7%B5%90%E3%81%8A%E3%82%88%E3%81%B3%E5%87%8D%E7%B5%90%E8%A7%A3%E9%99%A4) ## 参考 - [Delphi Tips - マルチスレッドアプリケーション (EDN)](https://support.embarcadero.com/jp/article/35961) - [スレッド管理ルーチン (DocWiki)](https://docwiki.embarcadero.com/RADStudio/ja/%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E7%AE%A1%E7%90%86%E3%83%AB%E3%83%BC%E3%83%81%E3%83%B3) - [Delphi でのマルチスレッドプログラミング (第11回 エンバカデロ・デベロッパーキャンプ【A2】)](https://edn.embarcadero.com/article/images/39112/a2.pdf) - [正しい GUI の作り方 (第16回 エンバカデロ・デベロッパーキャンプ【2C】)](https://edn.embarcadero.com/article/images/40483/2C.pdf) - [Delphi でキカイを制御する (第16回 エンバカデロ・デベロッパーキャンプ【2F】)](https://edn.embarcadero.com/article/images/40483/2F.pdf) - [TThread の Synchronize と Queue について (山本隆の開発日誌)](https://www.gesource.jp/weblog/?p=538) - [C++Builder 2009 のスレッドの基本的な使い方のまとめ (山本隆の開発日誌)](https://www.gesource.jp/weblog/?p=562) - [Delphi の TThread.CreateAnonymousThread と無名メソッドを使うと、簡単なスレッド処理なら手軽にかける (山本隆の開発日誌)](https://www.gesource.jp/weblog/?p=5093) - [C++ Builder / TThread > スレッドが終了時にソフトを終了する実装 (Qiita: @7of9)](https://qiita.com/7of9/items/0b78b5ef45cf0a09a6cd) - [C++ Builder / TTreahd > スレッド終了後の処理 (Qiita: @7of9)](https://qiita.com/7of9/items/705e7fde472ffcc4a7b7) - [C++ Builder > TThread:WaitFor() を使うときの注意 > FreeOnTerminate を false にしておく (Qiita: @7of9)](https://qiita.com/7of9/items/1155a8048989c290c64b) - [C++ Builder XE4 > TThread > Suspended() 後の Start() > 実行中または一時停止中のスレッドに対しては Start を呼び出せません (Qiita: @7of9)](https://qiita.com/7of9/items/09542ff8bace30712593) - [C++ Builder XE4 > TThread > 「実行中または一時停止中のスレッドに対しては Start を呼び出せません。」 > : TThread(/* CreateSuspended= */ true) で作ったスレッドの new 直後の Start は問題ない (Qiita: @7of9)](https://qiita.com/7of9/items/717c13b4202eed14357c) - [Delphi で指定フォルダ以下のファイル一覧を取得する (Qiita: @SequencePalladium)](https://qiita.com/SequencePalladium/items/0d2c4744f77b1e280a5b) - [Delphi TMessageManager (Qiita: @furudoi)](https://qiita.com/furudoi/items/08f02ca26a4385beb029) - [クラスメソッド版 TThread.Synchronize の使いどころ。 (Swanman's Horizon)](https://lyna.hateblo.jp/entry/20100209/1265643004) # 索引 [ [← 0. はじめに](./c6714835355a83cde088.md) ] [ [↑ 目次へ](./e8c1ff3a4c74e4c2a4f3.md) ] [ [→ 2. クリティカルセクションとロック](./9f3c7534e2ac406ba7dc.md) ] [^1]: `Resume()` および `Suspend()` メソッドは Delphi 2010 以降で非推奨となっており、使用すると ワーニング W1000 が発生します。.NET においてもこれらのメソッドは非推奨となっています ([Resume()](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.thread.resume) および [Suspend()](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.thread.suspend))。 [^2]: パラメータのないコンストラクタは Delphi XE 以降で利用可能です。 [^3]: `Start()` メソッドは Delphi 2010 以降で利用可能です。 [^4]: スタックサイズを指定可能なコンストラクタは Delphi XE 以降で利用可能です。 [^5]: クラスメソッド `NameThreadForDebugging()` は Delphi 2010 以降で利用可能です。 [^6]: ウィザードの [名前付きスレッド] は Delphi 7 以降で利用可能です。 [^7]: 無名メソッドに対応した `Synchronize()` は Delphi 2009 以降で利用可能です。 [^8]: `CreateAnonymousThread()` は Delphi XE 以降で利用可能です。 [^9]: クラスメソッドの `Synchronize()` は Delphi 7 以降で利用可能です。Delphi 2007 以降では `StaticSynchronize()` というクラスメソッドもありますが、単に `Synchronize()` を呼んでいるだけであり、現在では非推奨としてマークされています。 [^10]: `Queue()` メソッドは Delphi 2005 以降で利用可能です。Delphi 2007 以降では `StaticQueue()` というクラスメソッドもありますが、単に `Queue()` を呼んでいるだけであり、現在では非推奨としてマークされています。 [^11]: `TThread.Sleep()` メソッドは Delphi XE 以降で利用可能です。 [^12]: `SpinWait()` メソッドは Delphi 2010 以降で利用可能です。 [^13]: `CheckSynchronize()` メソッドは Delphi 6 以降で利用可能です。 [^14]: `Threads` デモは XE6 まで収録されていました。