🐥
C++BuilderでTEventを使用してスレッドの一時停止・再開を行う
1.TThreadのSuspend/Resumeメソッドは非推奨機能
従来のSuspend/Resumeメソッドは、デッドロックや未定義の振る舞いを引き起こす可能性があるため、使用すべきではない。
変わりにTEventやTMutexを使用することが推奨されている。
2.TEventを使用する
単純なサンプルプログラムを作ります。
5秒間操作がなければ"Timeout"を表示し、ボタンをクリックすると"Signal"を表示します。
まず、画面側を作ります。TEventを定義して、ボタンを押すと、TEventがONします。
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "System.SyncObjs.hpp" // TEvent用
#include "MyThread.h"
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
TEvent *event;
MyClass *myclass;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
event = new TEvent(NULL); // イベントの初期化
event->ResetEvent(); // イベントOFF状態にする
myclass = new MyClass(event); // スレッドを実行
myclass->Start();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
event->SetEvent(); // イベントON
}
次にスレッド側を実装します。
//---------------------------------------------------------------------------
#include <System.hpp>
#pragma hdrstop
#include "Unit1.h"
#include "MyThread.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
TEvent *event; // TEventポインタの定義
__fastcall MyClass::MyClass(TEvent *myevent)
: TThread(true)
{
event = myevent; // メインスレッド側のeventを受け取り
}
//---------------------------------------------------------------------------
void __fastcall MyClass::Execute()
{
//---- ここにスレッド コードを記述します ----
while (true){
// eventがOFFなら最大5秒間停止してelse条件に入る
// eventがONになると即時if条件に入る
if ( event->WaitFor(5000) == wrSignaled ) { // WaitFor(INFINITE)で無限待ち
Synchronize( NULL, [=](){ Form1->Memo1->Lines->Add("Signal!!"); } );
event->ResetEvent();
}else{
Synchronize( NULL, [=](){ Form1->Memo1->Lines->Add("Timeout!!"); } );
}
}
}
3.定期サイクル実行
スレッド側をTTimerのように定期的に実行させるにはいくつか方法がある。
3-1.Sleepを使用する
よくある方法でSleepを使用します。
void __fastcall MyClass::Execute()
{
while (true){
Sleep(5000);
// 次の処理を記述
}
}
3-2.eventのタイムアウトを利用する
eventをタイムアウトとして利用する。Sleepを使った方が直感的。
void __fastcall MyClass::Execute()
{
while (true){
event->WaitFor(5000); // 5秒停止 ※メインスレッドでeventをONしないことが条件
// 次の処理を記述
}
}
3-3.メインスレッド側のタイマでイベントを立てる
メインスレッドのTTimer-OnTimerイベントでSetEvent()を立てることでメインスレッドがトリガーを担当し、処理をスレッドで実行できる。サイクル外での突発的な駆動が可能。
void __fastcall MyClass::Execute()
{
while (true){
if ( event->WaitFor(5000) == wrSignaled ) { // WaitFor(INFINITE)で無限待ち
event->ResetEvent(); // 即フラグを落とすことで次のトリガーに備える
// 以下に処理コードを記述する
}
}
}
なかなか奥が深い。
Discussion