동기화 없는 쓰레드의 문제점 -----------------------------------------------------------------------------------

4개의 쓰레드가 각기의 연산을 한다고 했을때. 쓰레드들 사이의 메모리 전환이 단순히 프로그램의 라인라인 별로 이루어지지 않기 때문에 하나의 전역 변수를 가지고 다수의 쓰레드가 연산을 하게 된다면 그 연산과정에 있어서 CPU의 레지스터 값이 변경되는 순서가 프로그래머의 의도와는 다르게 움직인다.(아니 확실히 처음 쓰레드를 사용하는 사람의 생각과는 다르게 진행될 것이다.) 

LOAD & STORE 명령어를 제대로 이해했다면 이 부분이 이해될 것이다.


멀티 쓰레드 환경 ---------------------------------------------------------------------------------------------

멀티쓰레드 환경은 비쥬얼 2008버전에서 다음과 같이 설정이 가능하다.


또한 쓰레드 생성에 있어서 _beginthreadex 함수를 통해서 쓰레드를 생성해야 한다. 이함수와 일반 createthread 함수의 차이점은 한가지이다. _beginthreadex함수는 쓰레드 생성에 앞서서 생성하는 쓰레드를 위해서 독립적인 메모리 블록을 할당한다.

당연히 그 메모리 블록을 해제하기 위해서는 _endthreadex( unsigned retval )함수를 호출해야 한다.


쓰레드의 상태변화 --------------------------------------------------------------------------------------------

앞서 설명했던 그림을 한번더 깨내본다.

이 그림은 프로세스의 상태를 표현하기 위해서 예제가된 그림이지만 그 프로세스의 상태를 변경시키는 주체는 프로세스가 아니고 쓰레드 그 자체이다. 달라질 것은 없다. 프로세스안에 들어있는 쓰레드의 상태를 그대로 위의 그림과 연관시켜 생각해보면 간단하기 때문이다.

쓰레드가 생성되며 Start 상태가 되면 일단 ready상태가 되고 running 상태가 되길 기다린다. 그리고 running 상태로 실행중인 쓰레드에게 할당된 다임 슬라이스가 모두다 되면 다시 전환되고 자신이 cpu를 차지하지 않아도 되는 시점이 되면 blocked상태로 변하여 다른 쓰레드에게 CPU의 실행권한을 넘겨주고 다시 ready상태가 되면 된다.


상태변화 함수 --------------------------------------------------------------------------------------------

DWORD SuspendThread( HANDLE hThread ) 

쓰레드의 커널 오브젝트에는 SuspendThread 함수의 호출 빈도수를 기록하기 위한 서스팬드 카운트라 불리는 맴버가 존재한다.(Suspend Count) 현재 실행중인 쓰레드의 서스펜드 카운트는 0이다. 이 함수를 사용할때마다 Suspend Count를 1씩 증가시킨다고 보면 된다. 

Suspend Count는 당연히 반대로 감소시키는 함수가 존재한다.

DWORD ResumeThread( HANDLE hThread ) 

상태의 변화에 의한 내용은 다음과 같다.


Suspend Count가 의미하는 쓰레드의 상태 -----------------------------------------------------------------------

Suspend Count = 0 : 현재 실행중인 쓰레드(Ready)
Suspend Count > 0 : Block 상태

이 함수들은 변경되기 이전에 Suspend Count 를 리턴한다.

쓰레드의 우선순위 컨트롤 :
프로세스의 우선순위는 기존 우선순위라고 표현한다.
쓰레드는 추가로 상대적 우선순위를 갖는다.

 Priority  의미 
 THREAD_PRIORITY_LOWEST -2
 THREAD_PRIORITY_BELOW_NORMAL -1
 THREAD_PRIORITY_NORMAL 0 (Default)
 THREAD_PRIORITY_ABOVE_NORMAL +1
 THREAD_PRIORITY_HIGHEST

 +2



Posted by JJOREG

쓰레드의 소멸 ------------------------------------------------------------------------------------------------

일단 아래의 그림을 보자.


하나의 프로세스가 생성되면 그 프로세스의 메인쓰레드와 메인쓰레드에서 앞서 설명한 쓰레드생성 함수나 몇가지 방법을 통해서 쓰레드가 생성될 것이다. 메인쓰레드에 쓰레드 A B C는 종속적이라고 할수 있다. 이유는 아래 설명한다.

메인쓰레드의 특징은 메인쓰레드가 return 0 명령으로 종료되면 프로세스 그 자체가 종료된다는 것이다. 다른 쓰레드가 하던일을 끝마치기도 전에 종료가 될수 있다. 프로세스가 종료되면 당연히 쓰레드도 종료된다. 

하지만 다른 A B C의 return 문은 자신의 쓰레드(실행흐름)만 종료시키는 결과를 가진다.


쓰레드의 소멸방법 --------------------------------------------------------------------------------------------

1. 각 쓰레드 함수에서 return 문으로 종료하는 방법 -> (가장 이상적인 방법)

2. ExitThread 함수를 사용하는 방법 -> 이 함수는 현재 실행중인 쓰레드를 종료할때 사용한다.

VOID ExitThread( DWORD dwExitCode ); 가 함수의 원형이다.

이 방법의 장점은 언제 어디서나 자신이 원하는 쓰레드를 종료시킬수 있다는 점이다. 또한 다른 프로그래머가 쓰레드의 구조를 파악할때. 이 함수를 보고 쓰레드의 종료시점을 예측하기 편하다.



하지만 한가지 주의할 사항이 있다. C++(내가 사용하는 프로그래밍 언어) A, B 함수의 스택영역에 객체가 존재한다고 할때 C함수에서 ExitThread 함수가 호출된다면 A, B함수의 스택영역에 존재하는 객체의 소멸자가 호출되지 않아서 메모리릭이 날 수가 있다.

3. CreatdThread 함수를 이용해서 얻게되는 핸들을 통해서 다른 쓰레드를 종료하는 방법

만약 쓰레드의 핸들을 가지고 있다음 다음 함수를 이용해 쓰레드를 종료시킬수 있다.

BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode );

첫번째 인자는 당연히 종료시킬 쓰레드의 핸들이며 두번째 인자는 해당 쓰레드의 커널 오브젝트에 등록된다.

이 함수의 문제점은 쓰레드를 강제종료 시킨다는 점이다. 다시 말해서 종료의 대상이 되는 쓰레드는 종료가 되는 시점까지도 자신이 종료된다는 사실을 모르므로 당연히 할당된 메모리가 남아있건 어떤 상황이건 종료가 되어버린다.

책에도 나와있지만 그다지 권장하는 방법은 아니다.

Posted by JJOREG

쓰레드 생성 함수 ----------------------------------------------------------------------------------------------

HANDLE WINAPI CreateThread(
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in       SIZE_T dwStackSize,
  __in       LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in       DWORD dwCreationFlags,
  __out_opt  LPDWORD lpThreadId
);
함수 인자 

----------------------

1. __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, 

-> 쓰레드가 생성되면 당연히 그에 대한 핸들이 생성된다. 이 인자에 NULL이 전달되면 핸들은 자식 프로세스 생성 시 상속 대상에서 제외된다.

----------------------

2. __in SIZE_T dwStackSize,

-> 쓰레드 생성시 해당 쓰레드를 위한 스택이 별도 생성된다는 것은 앞서 설명되었다. 0이 전달되면 이 쓰레드의 스텍 크기는 1M로 적용된다.

----------------------

3. __in LPTHREAD_START_ROUTINE lpStartAddress,

-> 쓰레드로 동작하기 위한 함수, 다시 말해서 쓰레드의 MAIN함수 역할을 하는 함수를 지정하는 전달 인자이다. 정확히 말하자면 함수의 포인터가 인자값으로 들어가야만 한다. 이 인자는 다음과 같이 재정의 되어 있다.

typedef DWORD (WINAPI *PTHREAD_START_ROUTINE) (LPVOID lpThreadParameter);

typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE; 

따라서 반환타입이 DWORD이고 매개변수 타임은 LPVOID(void*)인 형태로 함수가 정의 되어야 한다.

----------------------

4. __in_opt LPVOID lpParameter,

-> 쓰레드 함수에 전달할 인자를 지정하는 용도로 사용한다. main 함수에서 argv로 문자열이 전달되는 것과 유사하다.

---------------------- 5. __in DWORD dwCreationFlags,

쓰레드의 생성 및 실행을 조절하기 위한 전달인자. 각 플레그가 존재하며 몇가지 설명하자면.

CREATE_SUSPENDED가 전달되면, 쓰레드는 생성과 동시에 Blocked 상태에 놓이게 된다. 그러나 ResumeThread가 호출되면 실행을 시작한다.

XP이상의 윈도우 버전에서는 STACK_SIZE_PARAM_IS_A_RESERVATION을 전달 할수 있는데 이 경우 dwStackSize를 통해 전달되는 값의 크기는 reserve 메모리 크기를 의미하게 되고, 그렇지 않은 경우는 commit 메모리 크기를 의미하게 된다.

---------------------- 6. __out_opt LPDWORD lpThreadId

쓰레드 id를 전달받기 위한 변수의 주소값을 전달한다. 굳이 필요 없다면 NULL을 전달해도 좋다. Windows ME이하 버전에서는NULL을 전달 할 수 없기 때문에 범용적인 사용을 위해서라도 주소값을 전달하는게 좋다.

7. 함수 호출이 성공하면 생성된 쓰레드의 핸들이 반환된다.


쓰레드 생성에 있어서 주의할 사항은 쓰레드는 메모리가 허용하는 만큼만 생성이 가능하다. 쓰레드가 하나 생성될 때마다 독립된 스택을 할당해야 하므로 스택을 할당할 수 있을 때까지 쓰레드의 생성이 허용된다.


또한 이 함수로 생성된 쓰레드는 커널영역에서 스케줄러에 의해서 관리된다.


Posted by JJOREG