>> 질문 : 초보님 (
bcbdn C++Builder Q&A 14813번 )
"for loop실행 중 Timer Event가 제대로 작동하나요?"
for loop가 약 30분 정도 돌아가야 되는데(기계제어 프로그램이기 때문에 for loop내에서
여러조건이 만족되어야 for loop내에서 다음 단계(step)로 넘어감) 이 과정중에 Timer Event를
사용할 필요가 있습니다. 즉 For Loop내에서 단계(step)가 진행될 때마다 global Variable이
변하게 되는데 이 때 이벤트를 걸어 다른 작업(Com2 Port를 통해 순간적으로 수신/전송을
해야 됨)을 해야 되기 때문이거든요. 그런데 for loop내에서 이 다른작업이라는 놈을 넣어니
최초 한번만 실행 되고 그 이후에는 반응이 없더라구요. 그래서 Timer event를 이용하여 일정
간격으로 이 다른작업이라는 놈을 실행되도록 했더니 되긴 되는데 제대로 되지 않더군요. 즉
Global Variable은 계속 바뀌는데 Timer Event 함수내에서 이 Global 변수를 추적해 보니까
Timer함수내로 이 변수가 제대로 넘어오지 않더군요. (이하생략)
>> 답변 : 지나가는사람 님 (
bcbdn C++Builder Q&A 14815번 )
Timer의 경우는 그런상태가 벌어지는 경우가 많습니다.
저도 비슷한 문제로 여러가지로 테스트를 해보았는데...
윈98 계열의 경우는 그러한 상태가 일어나는 경우가 상당히 많고..
NT 계열은 그나마 좀 낮습니다.
(98은 마우스로 캡션바를 드래그 & 드롭하는 경우에도 Timer는 멈춥니다.)
그렇지만.. NT라도 제대로 제어를 하기 위해서는 스레드는 필수입니다.
>> 추가 : for loop나 while문을 계속 돌리고 싶은 경우 빠져나오는 조건이 명확하지 않다면
Thread를 쓰시는 편이 시스템 전체가 다운되는 현상을 방지할 수 있습니다.
Thread는 File > New > Thread Object 이러한 과정을 생성하면 되구요. 예를 들면 아래와
같이 생성할 수도 있습니다.
//---------------------------------------------------------------------------
// 쓰레드 클래스 헤더 파일
//
class T_TestThread : public TThread
{
private:
__fastcall ~T_TestThread(void); // 클래스 파괴자
protected:
void __fastcall Execute(); // 쓰레스 동작시 실행코드 영역을
// 담은 pure virtual method
void __fastcall WaitTime(); // 동작하게될 method
// 인자가 있어서는 안된다.
public:
__fastcall T_TestThread(bool CreateSuspended); // 클래스 생성자
};
//---------------------------------------------------------------------------
// 쓰레드 클래스 CPP 파일
//---------------------------------------------------------------------------
__fastcall T_TestThread ::T_TestThread (bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
__fastcall T_TestThread ::~T_TestThread (void)
{
}
//---------------------------------------------------------------------------
void __fastcall T_TestThread ::Execute()
{
while(루프를 돌수있는 조건) // com->Enabled() 같이 적당한 조건
{
if(실행될 조건) // timer가 30분이 넘었다는...
Synchronize(WaitTime);
}
}
//---------------------------------------------------------------------------
void __fastcall T_TestThread ::WaitTime()
{
// com->write(buffer,size) 같이 실행 코드
}
//---------------------------------------------------------------------------
// 실행코드의 사용예
//
T_TestThread *testthread; // 전역선언
void __fastcall TForm1::Button1Click(TObject *Sender) // OnClick 이벤트에서
{
testthread = new T_TestThread(NULL); // T_TestThread 객체생성
testthread->Priority = tpLower; // 우선순위 결정
// com2->Open() 같이 조건을 만족시켜두고
testthread->Resume(); // 재시작
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender) // OnClick 이벤트에서
{
// com2->Close() 같이 루프를 빠져나오게 하고
while(testthread->WaitFor()) // 쓰레드 종료를 기다렸다가
;
testthread->Free(); // 종료가 되면 해제
}
//---------------------------------------------------------------------------
>> 참고
@ Execute
쓰레드 실행시에 실행 코드를 가지기 위한 순수 가상 메쏘드를 제공한다
(원형) virtual void __fastcall Execute(void) = 0;
(상세) 쓰레드 실행시 실행되어질 코드의 포함하고 실행하기위해 쓰인다. 이곳에서 쓰레드를
빠져나갈 조건을 체크한다.
CreateSuspended = false 라면 Create, CreateSuspended = true 라면 쓰레드를 생성 후에
Resume이 호출되었을 때 쓰레드는 실행한다.
(주의) 다른 오브젝트의 properties와 methods에서 직접적으로 쓰레드의 Execute를 사용하지
말라. 대신, procedure를 나눠서 Synchronize 메쏘드의 인자로써 procedure를 사용하라.
@ Synchronize
주요 VCL 쓰레드 호출 Executes method
(원형) typedef void __fastcall (__closure *TThreadMethod)(void);
void __fastcall Synchronize(TThreadMethod &Method);
(상세) Synchronize는 주 VCL 쓰레드를 사용하기위해 실행되어지는 메쏘드에만 호출되어야하기
때문에, 멀티-쓰레드에는 사용되지 말아야 한다. 쓰레드 호출이 안전할지 확실하지 않다면,
주 VCL 쓰레드를 거쳐서 Synchronize 메쏘드를 호출해라.
Execution 쓰레드는 주 VCL 쓰레드안에서 메쏘드가 실행될 동안에는 정지되어 있다.
(주의) critical section이나 multi-read exclusive-write synchronizer에서 사용되는 불안정한
메쏘드를 보호할 수 있다.
@ Priority
쓰레드 프로세스에서 상대적인 스케줄링 우선순위
(원형) enum TThreadPriority {tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical};
__property TThreadPriority Priority = {read=GetPriority, write=SetPriority,nodefault};
(상세) Priority는 쓰레드 스케줄링시에 사용되는 우선순위이다.
TThreadPriority형은 아래에 정의된 표에 따라 TThread 콤포넌트의 Priority property 값을
정의한다. Windows 우선순위 스케일에 기준으로 각 쓰레드의 CPU cycle을 스케쥴한다.
값 내용
tpIdle 시스템이 idle 상태일때만 실행된다. tpIdle의 우선순위를 가진
쓰레드의 실행때문에 다른 쓰레드 프로세스를 interrupt 하지는
않을 것이다.
tpLowest normal보다 2 point 낮은 우선순위를 가진다.
tpLower normal보다 1 point 낮은 우선순위를 가진다.
tpNormal normal 우선순위이다.
tpHigher normal보다 1 point 높은 우선순위를 가진다.
tpHighest normal보다 2 point 높은 우선순위를 가진다.
tpTimeCritical 최고 높은 우선순위를 가진다.
주의 : CPU 점유율이 높은 operation의 우선순위를 높이는 것은 동일 Application내에 타 쓰레드
가 동작하지 못하게 할 수도 있다. External event를 기다리는데 대부분의 시간을 사용하는 쓰레드
의 우선순위를 높여라.
@ WaitFor
쓰레드를 종료되기를 기다리고, 종료시 ReturnValue property를 return한다.
(원형) int __fastcall WaitFor(void);
(상세) WaitFor를 호출하면 쓰레드 종료 시 ReturnValue를 얻을 수 있다. WaitFor는 Execute
메쏘드가 끝나거나 Terminated property = true 가 되어 빠져나와서 쓰레드가 종료될때까지
return되지 않는다.