C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 팁&트릭
C++Builder Programming Tip&Tricks
[879] 쓰레드에서 일정시간 마다 메소드 실행.
김태선 [cppbuilder] 12723 읽음    2009-03-25 19:33
쓰레드가 고속으로 돌 때 어떤 값을 화면에 표시한다면
1초당 적당한 횟수를 표시를 하는 것이 좋습니다.
지나치게 빨리 많이 표시한다고 화면에 다 보이는 것도 아니고,
일정 프레임을 넘어가면 사람이 인지하는 한도를 넘어가기 때문에,
CPU 낭비해가며 표시할 필요는 없습니다.

쓰레드가 돌 때 특히 중요한 것이 바로 일정시간 마다, 사람이 보기에 매우 자연스럽게
화면이 갱신되는 것인데, 대략 TV나 영화가 화면을 갱신하는 것에 기준하는 것이 좋지 않나 생각됩니다.
그래서 1초에 영화는 24프레임, TV는 30프레임 갱신되므로,
대략 1초(1000 Milliseconds)에 25회~30회 정도 갱신하면 매우 자연스럽게 됩니다.
(물론 전문 동영상 화면의 갱신이라면 1초에 60회 갱신 등을 고려해야 보다 자연스러워 집니다)
이건 보통의 동영상일 경우고 숫자나 메시지를 갱신할 때는 보다 프레임을 낮추어도 상관이 없습니다.

쓰레드에서 적당히 갱신 횟수를 조절하는 방법으로는 아래 예제를 만들었는데 이러한 방법이 일반성이 있을 것 같군요.
하나는 함수를 써서 시간을 체크하는 방법이고, 또 하나는 직접 계산하는 방법인데,
둘다 DateUtils.hpp의 MilliSecondsBetween 함수를 사용해 매우 간편하게 시간차를 구합니다.
이는 매우 정밀한 8바이트 TDateTime 형 클래스를 사용하기 때문에 안심하고 사용할 수 있습니다.

보통 시간차를 구하는 방법으로 많이 쓰이는 것이 GetTickCount인데
GetTickCount는 DWORD로 4바이트 밀리세커 카운트를 돌립니다.
4바이트형이니 값이 마지막까지 차면 다시 0으로 돌아오게 됩니다.
그러므로 이를 그대로 가지고 시간차 계산을 해도 아무런 문제는 없습니다.
현재카운트 - 이전카운트 값 형태에서는 다시 카운트가 0을 지나 값이 올라가도
이상없이 그 시간차가 잘 구해집니다.
하지만 GetTickCount는 정밀도가 떨어진다고 알려져 있습니다.
TDateTime 형은 시간에서 바로 가져오므로 가져오는 순간의 값은 매우 정확하다고 볼수 있으나,
GetTickCount에서 가져오는 값은 시스템의 상황에 따라 오차가 발생한다고 합니다.
GetTickCount는 4바이트 값이라 49일즈음에는 증가되는 값이 한계에 도달해 다시 처음부터 다시 시작하게 됩니다.


//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop
#include <DateUtils.hpp>

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
// 테스트 쓰레드

class CThread : public TThread
{
private:
    // 함수로 일정 시간이 지났나 체크.
    //
    bool    IsTimeBetween(int interval, TTime& old_time)
    {
        TTime  now = Now();
        if (MilliSecondsBetween(now, old_time) >= interval)
        {
            old_time = now;
            return true;
        }
        return false;
    }
protected:
    TTime    Current;
    int        Count;
    int        CountMilli40;

    void __fastcall Execute()
    {
        while(!Terminated)
        {

            //  40밀리초 마다 화면 갱신하기. 1초에 화면 표시 25번 갱신.
            // 100밀리초 마다 화면 갱신하기. 1초에 화면 표시 10번 갱신.
            //
        #if 1
            if (IsTimeBetween(40, Current))
            {
                CountMilli40++;
                Synchronize(Display);
            }
        #else
            // 직접 체크.
            TTime  now = Now();
            if (MilliSecondsBetween(now, Current) >= 40)
            {
                Current = now;
                CountMilli40++;
                Synchronize(Display);
            }
        #endif
            else
            {
                Count++;
                Sleep(20);
            }
        }
    }
public:
    __fastcall CThread(bool CreateSuspended) : TThread(CreateSuspended)
    {
        FreeOnTerminate = true;
        Count = 0;
        CountMilli40 = 0;
        Current = Now();
    }
    void __fastcall Display()
    {
        String  s;
        s.printf("Current Count: %d    Count2: %d", CountMilli40, Count);
        Form1->Label1->Caption = s;
    }
};

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    CThread  *a = new CThread(false);
    a;
}
//---------------------------------------------------------------------------
망치 [mangchy]   2009-03-25 21:23 X
저는 쓰레드 루틴안에 WaitForSingleObject(event, time_msec);
해서 합니다. 그러면 event가 set이 되지 않는다면 time_msec동안 있다가 timeout되어서 다음을 실행합니다.
요즘 만드는 프로그램은 한 프로그램안에 플러그인 dll과 그 안에 쓰레드들이랑 엄청 돌아갑니다. 다행히 동기화나 소멸할때등 여러가지 문제점들을 해결하니 프로그램이 정말 부드러워지네요. cpu 점유율도 거의 0~10%안팍이구요.
김태선 [cppbuilder]   2009-03-25 23:12 X
그냥 일정시간마다 인터벌을 가지며 특정 루틴을 실행한다면
좋은 방법이네요.

그런데, 위에 것은 쓰레드를 돌며 다른 일을 하면서
일정시간 마다, 연관된 특정한 메소드를 실행하는 것에 초점이 맞추어진 것입니다.
김시환 [godson2]   2009-03-27 10:10 X
사용하고자 하는 용도에 따라 틀리겠지만 저의 경우에는 쓰레드에서는 메모리에 변화값만 저장하고 폼 객체에서 타이머를 이용하여 뿌려 줍니다. 제 경우는 뭐 1초에 한번 뿌려도 무관한 프로그램이라서 가능한 CPU 부하를 줄이려고 이런식으로 합니다.
김태선 [cppbuilder]   2009-03-27 11:15 X
그것도 좋은 방법이네요.
저는 대표적으로 쓰레드에서 화면 갱신의 경우 시간을 따지는 일이 많으닌까
그걸 예로 들은 것인데, 화면 갱신을 주 목적으로 한다면
김시환님처럼 하는 것도 한 방법이 되겠네요.
김태선 [cppbuilder]   2009-03-29 08:58 X
GetTickCount은 16bit 윈도 OS시절부터 있었던 고전 API라서,
오차가 시스템에 따라 차이가 있으나 약 16ms 정도의 최소 오차를 기대할 수 있다고 하네요.
그러닌까 실제 오차는 훨씬 더 큰 감수를 해야하고, 시스템이 큰 부하에 걸리면 더 큰 오차가 생긴다는 뜻이지요.

+ -

관련 글 리스트
879 쓰레드에서 일정시간 마다 메소드 실행. 김태선 12723 2009/03/25
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.