WinMain과 메시지 루프, WndProc, 일반 함수들을 오가며 메시지를 처리하는 일련의 코드들을 스레드 라고한다.

프로세스는 단지 존재하기만 하는 껍데기일 뿐, 실제 작업은 스레드가 담당한다. 프로세스 생성시 하나의 주 스레드가 생성되며 대부분의 경우 주 스레드가 모든 작업을 처리하고 주 스레드가 종료되면 프로세스도 같이 종료된다.
주스레드 = 프로세스


그리고 주스레드 실행도중에 다른 작업을 같이 하고싶으면 스레드를 여러개 더 추가 할 수 있는데 그 스레드들을 실행하는 도중에 주스레드가 종료되면 실행중인 보조 스레드들도 다 종료된다.


스레드생성

HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)


LPSECURITY_ATTRIBUTES lpThreadAttributes - 스레드의 보안 속성을 지정하는데 자식 프로세스로 핸들을 상속하지 않는 한 NULL로 지정하면 된다.

DWORD dwStackSize - 스레드의 스택 크기를 지정하는데 스레드끼리 상호 안정된 동작을 하기 위해 스레드별로 별도의 스택이 할당된다. 스택의 초기 크기를 0으로 지정하면 주 스레드(Create Thread를 호출한 스레드) 와 같은 크기를 가진다. (기본적으로 스레드의 스택은 1M가 예약되며 프로세스의 주소 공간은 2G이므로 생성 가능한 스레드 개수는 약 2000개이다.)

LPTHREAD_START_ROUTINE lpStartAddress -  스레드의 시작 함수를 지정 

스레드 시작함수의 원형 - DWORD WINAPI ThreadFunc(LPVOID lpParameter)

LPVOID lpParameter - 시작 함수로 전달할 작업내용이다. 없을 경우 NULL

DWORD dwCreationFlags - 생성할 스레드의 특성을 지정하는데 0이면 물론 아무 특성 없는 보통 스레드이다. CREATE_SUSPENDED 플래그를 지정하면 스레드를 만들기만 하고 실행은 하지 않는다. 중지된 스레드를 실행 할때는 Resume Thread 함수를 호출한다.

LPDWORD lpThreadId - 스레드를 만든 후 스레드의 ID를 리턴하기위한 출력용 인수로이므로 DWORD형의 변수를 하나 선언한 후 그 변수의 번지를 넘기면 된다. 스레드 ID가 필요한 경우는 별로 없는데 이 경우는 NULL을 전달한다.

CreateThread 함수는 스레드를 만든 후 스레드의 핸들을 리턴하며 에러가 발생했을 경우 NULL을 리턴한다. 리턴된 스레드의 핸들은 이후 이 스레드를 제어하고자 할 때 사용하는데 생성 후 스레드를 더 이상 조작하지 않을 경우 곧바로 핸들을 닫아도 된다. 스레드 핸들과 스레드 자체는 다르므로 핸들을 닫는다고 해서 스레드가 종료되는 것은 아니다. 여섯 번째 인수로 스레드 ID도 리턴 되는데 핸들과 ID의 차이에 대해서 아래에 간략히 적어보겠다.



핸들 - 프로세스 내에서 해당 객체를 액세스할 때 사용하는 한정적인 값이며 이 핸들을 사용하여 객체를 마음대로 조작할 수 있다. (C++의 지역변수)
ID -  시스템 전역적인 값이며 다른 프로세스 ID와 절대 중복되지 않는다. 그래서 프로세스끼리 ID를 전달함으로써 목적이 되는 프로세스 핸들을 다시 오픈할 수 있다. 실행 중인 프로세스의 ID는 작업 관리자에서 쉽게 확인할 수 있다.
(C++에서 Get 함수)

정리 - 프로세스 ID는 프로세스간의 구분을 위한 중복되지 않는 식별값일 뿐이며 ID로부터 직접 프로세스를 제어할 수는 없다. ID로부터 핸들을 발급받아야만 비로소 이 객체를 제어할 수 있다.


스레드종료

자식 쓰레드가 작업을 할 때 주 쓰레드는 자식 쓰레드를 만들기만 하고 종료 상태에는 별로 관심을 두지 않는 것이 보통이다. 특별한 경우를 제외하고 두 쓰레드는 서로 독립적으로 실행될 뿐이다. 그러나 주 쓰레드는 적어도 자식 쓰레드가 종료되었는지의 여부는 주기적으로 조사해 봐야 한다. 이때 사용되는 함수가 GetExitCodeThread 함수이다.

 

BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode);

 

hThread

쓰레드의 핸들 값

lpExitCode

쓰레드의 종료 코드조사를 위한 인수

 

 

때로는 작업 중간에 쓰레드를 종료해야 하는 경우가 있다. 예를 들어 다운로드를 받는 스레드를 만들었는데 중간에 사용자가 다운로드를 취소했다면 더 이상 이 쓰레드는 존재할 필요가 없다. 

쓰레드를 강제 종료할 때 사용되는 함수는 ExitThread 이다.

 

VOID ExitThread (DWORD dwExitCode);

ExitThread 는 쓰레드가 스스로 종료할 때 사용하는데 인수로 종료 코드를 넘겨준다. 쓰레드가 ExitThread 를 호출하면 자신의 스택을 해제하고 연결된 DLL을 모두 분리 후 파괴된다.

TerminateThread는 쓰레드 핸들을 인수로 전달받아 해당 쓰레드를 강제로 종료한다.

 





'API & MFC > API & 시스템프로그래밍' 카테고리의 다른 글

WNDCLASS 구조체  (0) 2010.01.25
스레드의 함정  (0) 2010.01.25
이벤트  (0) 2010.01.24
세마포어  (0) 2010.01.24
파일매핑 사용순서  (0) 2010.01.24
Posted by 아몰라

This는 자기 자신을 가르키는 용도로 사용된다. 그래서 자기 참조 포인터라 한다.

class Member 
{

 int Test1;
 int Test2;
public:
 Member(int Test1, int Test2 )
 {
  this->Test1 = Test1;
  this->Test2 = Test2;
 }
 virtual ~Member();
 void View()
 {
  cout<<Test1 <<endl <<Test2<<endl;
 }
 

};
 

int main()
{
 Member M(1, 2);
 M.View();
 return 0;
}


Test1, Test2 은 매개 변수 와 클래스 멤버변수와 이름이 겹친다

하지만 매개변수는 지역변수이므로 그 안에서 멤버변수 Test에는 접근할 수 없다

방법은 this 포인터를 이용해서 해당 객체의 멤버변수를 직접 가르키면 된다


class Member 
{

public:
 
 Member * GetThis()
 {
  return this;
 }
 

};

int main()
{
 Member * p1 = new Member();
 cout<<"포인터 p1 " << p1<<endl;
 cout<<"p1 의 this " << p1->GetThis()<<endl;

 Member * p2 = new Member();
 cout<<"포인터 p2 " << p2<<endl;
 cout<<"p2 의 this " << p2->GetThis()<<endl;

 return 0;
}



'프로그래밍 기초 > C++' 카테고리의 다른 글

반올림함수 만들기  (0) 2010.05.11
가상함수테이블  (0) 2010.03.04
다형성  (0) 2010.03.04
템플릿이란!?  (0) 2009.11.20
Posted by 아몰라


이벤트란 어떤 사건이 일어났음을 알리는 동기화 객체이다?

 

-       크리티컬섹션, 뮤텍스, 세마포어는 주로 공유자원을 보호하기 위해 사용되는 데 비해 이벤트는 그보다는 스레드간의 작업 순서나 시기를 조정하고 신호를 보내기 위해 사용한다.

 

 

이벤트는 윈도우의 메시지와 유사하다?

 

-       사용자가 키보드 누를 때 WM_KETDOWN 메시지를 윈도우 프로시저에게 보내 처리하게 하는 것처럼 정렬이나 다운로드가 끝났을 때 이벤트를 보내 관련된 다른 작업을 하도록 지시할 수 있다.

 

 

자동 리셋 이벤트

대기 상태가 종료되면 자동으로 비신호상태가 된다.

수동 리셋 이벤트

스레드가 비신호상태로 만들 때까지 신호상태를 유지한다.

 

 

이벤트 함수정리

 

이벤트생성함수

 

HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL blnitialState, LPCTSTR lpname);

 

lpEventAttributes

보안속성

bManualReset

TRUE이면 수동 리셋 이벤트 FALSE이면 자동 리셋 이벤트

blnitialState

TRUE이면 이벤트를 생성함과 동시에 신호상태로 만든다.

lpname

이름

세마포어와 마찬가지로 첫번째, 네번째인자는 프로세스동기화와 관련있다.

 

 

n  이벤트가 뮤텍스나 크리티컬섹션과 또 다른 점은 대기 함수를 사용하지 않고도 임의적으로 신호상태나 비신호상태를 설정할 수 있다는 점이다. 이때 쓰는 함수가 아래와 같다.

 

신호상태변경함수

 

BOOL SetEvent(HANDLE hEvent)

BOOL ResetEvent(HANDLE hEvent)

 

SetEvent

신호상태로 만든다.

ResetEvent

비신호상태로 만든다.

이런 상태변화가 대기중인 스레드에게는 일종의 신호로 전달된다.

 

 

 

자동 리셋 이벤트

 

       대기 상태를 풀 때 자동으로 이벤트를 비신호상태로 만든다는 뜻이다.

       여러 개의 스레드가 하나의 이벤트를 기다리고있을때 적합하지않다.

 

 

수동 리셋 이벤트

 

       대기 상태를 풀 때 신호 상태를 그대로 유지하고 ReSerEvent 함수로 일부러 비신호상태로 만들 때만 상태가 변경된다.

        여러 개의 스레드가 하나의 이벤트를 기다리고있을때 적합하다.



함수 사용 예제

 

HANDLE hEvent;

DWORD WINAPI ThreadSend(LPVOID temp)

{

             WaitForSingleObject(hEvent,INFINITE); //신호상태가 될때까지 무한정대기

             HDC hdc=GetDC(hWndMain);

             TextOut(hdc,210,100,"전송완료",8);

             ReleaseDC(hWndMain, hdc);

             return 0;

}

 

DWORD WINAPI ThreadSave(LPVOID temp)

{

             WaitForSingleObject(hEvent,INFINITE); //신호상태가 될때까지 무한정대기

             HDC hdc=GetDC(hWndMain);

             TextOut(hdc,110,100,"저장완료",8);

             ReleaseDC(hWndMain, hdc);

             return 0;

}

 

DWORD WINAPI ThreadCalc(LPVOID temp)

{

             HDC hdc=GetDC(hWndMain);

             for (int i=0;i<10;i++) {

                           TextOut(hdc,10,50,"계산중",6);

                           GdiFlush();

                           Sleep(300);

                           TextOut(hdc,10,50,"기다려",6);

                           GdiFlush();

                           Sleep(300);

             }

             TextOut(hdc,10,50,"계산완료",8);

             ReleaseDC(hWndMain, hdc);

             SetEvent(hEvent);   //신호상태로 바꿔줌

             return 0;

}

 

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

             HDC hdc;

             PAINTSTRUCT ps;

             DWORD ThreadID;

             TCHAR *Mes="마우스 왼쪽 버튼을 누르면 계산을 시작합니다";

 

             switch (iMessage) {

             case WM_CREATE:

                           hWndMain=hWnd;

                           hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);  //수동이벤트

                          

                           //hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); // 자동 이벤트

                           return 0;

             case WM_LBUTTONDOWN:

                           InvalidateRect(hWnd,NULL,TRUE);

                           ResetEvent(hEvent);

                           CloseHandle(CreateThread(NULL, 0, ThreadCalc, NULL, 0, &ThreadID));

                           CloseHandle(CreateThread(NULL, 0, ThreadSave, NULL, 0, &ThreadID));

                           CloseHandle(CreateThread(NULL, 0, ThreadSend, NULL, 0, &ThreadID));

                           return 0;

             case WM_PAINT:

                           hdc=BeginPaint(hWnd, &ps);

                           TextOut(hdc,10,10,Mes,lstrlen(Mes));

                           EndPaint(hWnd, &ps);

                           return 0;

             case WM_DESTROY:

                           CloseHandle(hEvent);

                           PostQuitMessage(0);

                           return 0;

             }

             return(DefWindowProc(hWnd,iMessage,wParam,lParam));

 

WM_CREATE에서 이벤트 객체를 생성하는데 이 때 두 번째 인수로 TRUE를 지정하여 수동 리셋 이벤트가 되도록하였다.

 

가장 위 에 있는 계산스레드가 실행이되는데 계산이 완료되면 SetEvent함수를 호출하여 신호상태로 만든다.

신호상태로 바뀌어지면 대기하고있던 스레드들이 모두 실행 할 수 있다.

 

, 자동리셋은 같은 상황에서 계산스레드가 끝나고 다른 스레드가 실행되는데 그 스레드가 대기함수를 거치면서 신호를 비신호상태로 바꿔놓음으로서 다른 스레드들은 또 대기를 해야한다.



출처 - 윈도우즈 API 정복 , 한빛미디어

'API & MFC > API & 시스템프로그래밍' 카테고리의 다른 글

스레드의 함정  (0) 2010.01.25
스레드  (0) 2010.01.24
세마포어  (0) 2010.01.24
파일매핑 사용순서  (0) 2010.01.24
Dll이란?  (0) 2010.01.24
Posted by 아몰라