# <8> イベント (同期オブジェクト) (Delphi コンカレントプログラミング) --- tags: Delphi プログラミング Pascal embarcadero objectpascal created_at: 2021-12-05 updated_at: 2024-05-22 --- # 8. イベント (同期オブジェクト) **イベント (Event)** は同期オブジェクトの一つです。待機中の任意のスレッドのアクセスを許可します。 ## 8.1. TEvent `TEvent` クラス [^1] は `System.SyncObjs` で定義されています。 最初の書式のコンストラクタでは、名前付きのイベントを作成します (名前が空でない場合はシステムイベント)。`ManualReset` パラメータが True だと、手動イベントとなり、`ResetEvent()` メソッドを実行しないと非シグナル状態になりません。False だと自動リセットとなり、`WaitFor()` メソッドで待機しているスレッドが処理を始めると非シグナル状態になります。`InitialState` プロパティはシグナルの初期状態です。True でシグナル状態、False で非シグナル状態です。 二番目の書式のコンストラクタを使うと、無名イベントを作成します。無名イベントはローカルイベントです。手動リセット、非シグナル状態で作成されます。 ```pascal constructor Create(EventAttributes: PSecurityAttributes; ManualReset, InitialState: Boolean; const Name: string; UseCOMWait: Boolean = False); constructor Create(UseCOMWait: Boolean = False); ``` 最初期のこのクラスは Windows API をラップしたものでした。 | Delphi | Windows API | |:---|:---| | [TEvent.Create()](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TEvent.Create) コンストラクタ | [CreateEventA()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa) / [CreateEventW()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventw)| | [TEvent.WaitFor](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.THandleObject.WaitFor) プロパティ| [WaitForSingleObject()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject) | | [TEvent.SetEvent()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TThread.Resume) メソッド | [SetEvent()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setevent) | | [TEvent.ResetEvent()](https://docwiki.embarcadero.com/Libraries/Sydney/ja/System.Classes.TThread.Suspend) メソッド | [ResetEvent()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-resetevent) | `WaitFor()` メソッドで待機しているスレッドに対して、`SetEvent()` でイベント (非シグナル -> シグナル) を通知する事ができます。 **See also:** - [System.SyncObjs.TEvent (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TEvent) ## 8.1.1. ローカルインスタンスで使う TEvent イベントは同期オブジェクトなので、通常はグローバルインスタンスとして作成するのですが、`TThread` のフィールドとして使うと面白い事が出来ます。 次のようなユニットを作ります。 ```pascal:uEVThread.pas unit uEVThread; interface uses System.Classes, System.SyncObjs, Vcl.StdCtrls; type TEvThread = class(TThread) private FEvent: TEvent; FComponent: TComponent; protected procedure TerminatedSet; override; public constructor Create(Component: TComponent); overload; destructor Destroy; override; procedure Stop; property Event: TEvent read FEvent write FEvent; property Component: TComponent read FComponent write FComponent; end; implementation { TEvThread } constructor TEvThread.Create(Component: TComponent); begin FEvent := TEvent.Create; FComponent := Component; FreeOnTerminate := True; inherited Create(True); end; destructor TEvThread.Destroy; begin FEvent.Free; inherited; end; procedure TEvThread.Stop; begin Terminate; FEvent.SetEvent; end; procedure TEvThread.TerminatedSet; begin inherited; FEvent.SetEvent; end; end. ``` フォームにメモとボタンが二つあるアプリケーションを作ります。 ![image.png](./images/ea2cc8f4-4a3e-eb5a-b05f-0e659c1b93e1.png) ```pascal uses ..., uEvThread; type TMyThread = class(TEvThread) protected procedure Execute; override; end; ... { TMyThread } procedure TMyThread.Execute; begin inherited; if not Terminated then begin TThread.Sleep(5000); Synchronize( procedure begin (Component as TMemo).Lines.Add('Start'); end ); end; end; ``` 二つのボタンのイベントハンドラはこうなります。 ```pascal // [Start] ボタン procedure TForm1.Button1Click(Sender: TObject); begin Memo1.Clear; MyThread := TMyThread.Create(Memo1); MyThread.Start; end; // [Stop] ボタン procedure TForm1.Button2Click(Sender: TObject); begin MyThread.Stop; // または MyThread.Terminate; Memo1.Lines.Add('Stop'); end; ``` [Start] ボタンを押してすぐに [Stop] ボタンを押しても、`Sleep(5000)` はキャンセルされないため、'Start' は [Start] ボタンを押して 5 秒後に表示されます。 ![image.png](./images/555d3378-a2b0-2e61-18d5-54e8c02836c0.png) 次に `Execute()` メソッド内の `Sleep(5000)` を `Event.WaitFor(5000)` に変更してみます。 ```pascal procedure TMyThread.Execute; begin inherited; if not Terminated then begin // TThread.Sleep(5000); Event.WaitFor(5000); // <- Synchronize( procedure begin (Component as TMemo).Lines.Add('Start'); end ); end; end; ``` 今度は [Stop] ボタンを押すとすぐに 'Start' が表示されたと思います。もちろん、'Start' を表示しないようにする事もできます。 ```pascal procedure TMyThread.Execute; begin inherited; if not Terminated then begin Event.WaitFor(5000); if not Terminated then // <- Synchronize( procedure begin (Component as TMemo).Lines.Add('Start'); end ); end; end; ``` ![image.png](./images/69732d72-2c82-17f8-6b79-3c49bbbb0ec9.png) **See also:** - [Implement override for thread Terminate method (StackOverflow)](https://stackoverflow.com/questions/33962710/implement-override-for-thread-terminate-method) ## 8.2. TSimpleEvent `TSimpleEvent` クラス [^2] は `System.SyncObjs` で定義されています。 現在の実装では `TSimpleEvent` クラスと `TEvent` クラス (の無名イベント) に違いはありません [^3] 。以前もコンストラクタのみが異なっていました。 ```pascal constructor TSimpleEvent.Create; begin inherited Create(nil, True, False, ''); end; ``` **See also:** - [System.SyncObjs.TSimpleEvent (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TSimpleEvent) ## 8.3. TLightweightEvent `TLightweightEvent` クラス [^4] は軽量なイベントです。`System.SyncObjs` で定義されています。但し、ローカルイベントとしてしか使えません。 .NET の `ManualResetEventSlim` クラスに相当します。 **See also:** - [System.SyncObjs.TLightweightEvent(DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TLightweightEvent) - [ManualResetEventSlim クラス (docs.microsoft.com)](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.manualreseteventslim) #参考 - [排他制御におけるイベント (Wikipedia)](https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)#%E6%8E%92%E4%BB%96%E5%88%B6%E5%BE%A1%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88) # 索引 [ [← 7. セマフォ (同期オブジェクト)](./6d7aa5ab5856d231b79e.md) ] [ [↑ 目次へ](https://qiita.com/items/e8c1ff3a4c74e4c2a4f3) ] [ [→ 9. 非同期プログラミング ライブラリ (APL)](./eedf3728be2a301a8a5d.md) ] [^1]: `TEvent` は Delphi 3 以降で利用可能です。 [^2]: `TSimpleEvent` は Delphi 3 以降で利用可能です。 [^3]: `TSimpleEvent` は Delphi 2005 以降、`TEvent` のエイリアスとなっています。 [^4]: `TLightweightEvent` は Delphi XE 以降で利用可能です。