7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DelphiAdvent Calendar 2021

Day 15

<8> イベント (同期オブジェクト) (Delphi コンカレントプログラミング)

Last updated at Posted at 2021-12-14

8. イベント (同期オブジェクト)

イベント (Event) は同期オブジェクトの一つです。待機中の任意のスレッドのアクセスを許可します。

8.1. TEvent

TEvent クラス 1System.SyncObjs で定義されています。

最初の書式のコンストラクタでは、名前付きのイベントを作成します (名前が空でない場合はシステムイベント)。ManualReset パラメータが True だと、手動イベントとなり、ResetEvent() メソッドを実行しないと非シグナル状態になりません。False だと自動リセットとなり、WaitFor() メソッドで待機しているスレッドが処理を始めると非シグナル状態になります。InitialState プロパティはシグナルの初期状態です。True でシグナル状態、False で非シグナル状態です。

二番目の書式のコンストラクタを使うと、無名イベントを作成します。無名イベントはローカルイベントです。手動リセット、非シグナル状態で作成されます。

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() コンストラクタ CreateEventA() / CreateEventW()
TEvent.WaitFor プロパティ WaitForSingleObject()
TEvent.SetEvent() メソッド SetEvent()
TEvent.ResetEvent() メソッド ResetEvent()

WaitFor() メソッドで待機しているスレッドに対して、SetEvent() でイベント (非シグナル -> シグナル) を通知する事ができます。

See also:

8.1.1. ローカルインスタンスで使う TEvent

イベントは同期オブジェクトなので、通常はグローバルインスタンスとして作成するのですが、TThread のフィールドとして使うと面白い事が出来ます。

次のようなユニットを作ります。

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

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;

二つのボタンのイベントハンドラはこうなります。

// [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

次に Execute() メソッド内の Sleep(5000)Event.WaitFor(5000) に変更してみます。

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' を表示しないようにする事もできます。

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

See also:

8.2. TSimpleEvent

TSimpleEvent クラス 2System.SyncObjs で定義されています。

現在の実装では TSimpleEvent クラスと TEvent クラス (の無名イベント) に違いはありません 3 。以前もコンストラクタのみが異なっていました。

constructor TSimpleEvent.Create;
begin
  inherited Create(nil, True, False, '');
end;

See also:

8.3. TLightweightEvent

TLightweightEvent クラス 4 は軽量なイベントです。System.SyncObjs で定義されています。但し、ローカルイベントとしてしか使えません。

.NET の ManualResetEventSlim クラスに相当します。

See also:

#参考

索引

[ ← 7. セマフォ (同期オブジェクト) ] [ ↑ 目次へ ] [ → 9. 非同期プログラミング ライブラリ (APL) ]

  1. TEvent は Delphi 3 以降で利用可能です。

  2. TSimpleEvent は Delphi 3 以降で利用可能です。

  3. TSimpleEvent は Delphi 2005 以降、TEvent のエイリアスとなっています。

  4. TLightweightEvent は Delphi XE 以降で利用可能です。

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?