"스레드 동기화가 필요한 대부분의 프로그램은 이런 간단한 시나리오만을 정확히 해결하면 되는 경우가 많으며, 복잡한 문제를
내포하는 경우는 그다지 많지 않다. "
그렇기 때문에 멀티스레드 소프트웨어를 작성할때는 디자인 패턴에 해당되는지 한번 살펴본후, 여기에서 제시하는
해결책을 사용한다면 좋은 프로그램을 작성할 수 있다.
자원의 상호 배재
상호배제는 ( mutual exclusion ) 는 스레드 동기화의 가장 기초적인 디자인 패턴이다.
데이터나 코드와 같은 시스템 자원이 동시에 둘 이상의 스레드에 의해서 안전한 방법으로 접근될수 없을때 상호 배제가 사용된다.
한번에 하나의 스레드 만이 자원에 접근하도록 허용하고 싶은 경우에는 이 자원을 동기화 객체를 사용해서 보호해야 한다.
- 직렬화 (serializing ) : 첫번째 스레드가 자원을 사용하는 동안 두번째 스레드가 접근하려고 하면 둘째 스레드는 블록되고
수행이 보류된다. 이러한 방법으로 자원에 접근하는것이 serializing 이라고 한다. - win32 환경에서는 직렬화 하기 위해서 두개의 시스템 객체 임계영역과 뮤텍스를 사용한다.
- 임계영역은 부하가 적으며, 한 프로세스 안의 스레드 사이에서 사용될 때 유용하다.
- 뮤텍스는 다수의 프로세스에서 동작하는 스레드가 사용하는 자원에 사용되며 일반적으로 실행시간의 비용이 많이 든다.
|
- 다수의 스레드가 이런 함수들을 동시에 호출할 수 있기 때문에 하나의 스레드가 AddRedThread 를 호출하는 동안 다른 스레드가 GetReadThreadCount 를 호출하는 경우도 발생할 수 있다.
- 또한 AddThreadCount 를 실행하는 스레드가 MyThreadInfo 의 hRedThreads 멤버를 증가시켯을지라도 GetRedThreadCount 를 호출한 스레드가 데이터를 읽을때 nTotalThreads 를 미처 증가 시키지 못했을수도 있다.
- 이런 경우가 발생하면 red 스레드의 개수와 모든 스레드의 개수가 일치하지 않을것이가.
- 결국 요구되는 것은 red 스레드의 개수와 모든 스레드의 개수를 동시에 읽는것이다.
- 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192CMlcCriticalSection g_CriticalSecThreadInfo;int main(int argc, char* argv[]){return 0;}void InitializedMyThreadInfo(void){g_myThreadInfo.nTotalThreads = 0;g_myThreadInfo.nRedThreads = 0;g_myThreadInfo.nWhiteThreads = 0;g_myThreadInfo.nBlueThreads = 0;}void AddRedThread(void){g_CriticalSecThreadInfo.Enter();g_myThreadInfo.nTotalThreads++;g_myThreadInfo.nRedThreads++;g_CriticalSecThreadInfo.Leave();}void AddWhiteThread(void){g_CriticalSecThreadInfo.Enter();g_myThreadInfo.nTotalThreads++;g_myThreadInfo.nWhiteThreads++;g_CriticalSecThreadInfo.Leave();}void AddBlueThread(void){g_CriticalSecThreadInfo.Enter();g_myThreadInfo.nTotalThreads++;g_myThreadInfo.nBlueThreads++;g_CriticalSecThreadInfo.Leave();}void RemoveRedThread(void){g_CriticalSecThreadInfo.Enter();g_myThreadInfo.nTotalThreads--;g_myThreadInfo.nRedThreads--;g_CriticalSecThreadInfo.Leave();}void RemoveWhiteThread(void){g_CriticalSecThreadInfo.Enter();g_myThreadInfo.nTotalThreads--;g_myThreadInfo.nWhiteThreads--;g_CriticalSecThreadInfo.Leave();}void RemoveBlueThread(void){g_CriticalSecThreadInfo.Enter();g_myThreadInfo.nTotalThreads--;g_myThreadInfo.nRedThreads--;g_CriticalSecThreadInfo.Leave();}int GetRedThreadCount(int *pnTotalThreads){g_CriticalSecThreadInfo.Enter();int nReadThread = g_myThreadInfo.nRedThreads;*pnTotalThreads = g_myThreadInfo.nTotalThreads;g_CriticalSecThreadInfo.Leave();return nReadThread;}int GetWhiteThreadCount(int *pnTotalThreads){g_CriticalSecThreadInfo.Enter();int nReadThread = g_myThreadInfo.nRedThreads;*pnTotalThreads = g_myThreadInfo.nTotalThreads;g_CriticalSecThreadInfo.Leave();return nReadThread;}int GetBlueThreadCount(int *pnTotalThreads){g_CriticalSecThreadInfo.Enter();int nReadThread = g_myThreadInfo.nRedThreads;*pnTotalThreads = g_myThreadInfo.nTotalThreads;g_CriticalSecThreadInfo.Leave();return nReadThread;return 0;}
cs
- 이러한 요구사항은 MyThreadInfo 구조체에 대한 접근을 직렬화 해서 쉽게 해결할 수 있다
+ 잘설계된 C++ 프로그램에서는 이런 문제를 해결하기 위해서 데이터 구조와 임계 영역을 하나로 묶고, 이 임계 영역을 사용하는 루틴을 하나의 클래스로 만들며, 애플리케이션 안에
서 전역적으로 사용할 수 있는 이 클래스의 인스턴스를 생성한다.
- 123456789101112131415161718192021222324252627282930313233343536373839404142class CMyThreadInfo{int m_nTotalThreads;int m_nRedThread;int m_nWhiteThread;int m_nBlueThread;CMlcCriticalSection m_CricSec;public:CMyThreadInfo() {m_nTotalThreads = 0;m_nRedThread = 0;m_nWhiteThread = 0;m_nBlueThread = 0;};void AddRedThread(){m_CricSec.Enter();m_CricSec.m_nTotalThreads++;m_CricSec.m_nRedThread++;m_CricSec.Leave();};void AddWhiteThread(){m_CricSec.Enter();m_CricSec.m_nTotalThreads++;m_CricSec.m_nWhiteThread++;m_CricSec.Leave();};void AddBlueThread(){m_CricSec.Enter();m_CricSec.m_nTotalThreads++;m_CricSec.m_nBlueThread++;m_CricSec.Leave();};};
cs
- CMyThreadInfo 클래스는 자신만의 동기화를 제공한다. 이 클래스의 사용자는 동기회 되어 사용되는 데이터에 접근하는 것에 대해서는 알 필요가 없다.
- 이런 동기화를 내부 동기화(Internal synchronization ) 이라 한다.
- 내부 동기화는 자원의 모든 사용자들이 정해진 방법을 통해서 자원을 사용할 때 유용하다.
- 비슷한 기법으로 외부 동기화가 있다.
- 이 기법은 자원을 사용하는 소프트웨어 사이에서 다수의 객체가 동기화 될때 사용한다.
- 상호배제의 다른 사용 예는 동시에 하나 이상의 스레드에 의해서 특정 코드 영역이 사용되는 것을 막기 위한 경우이다.
- 이런 기법은 프로세스에서도 사용될수 있으므로 매우 유용하다.
- 가장 일반적이고 가장 간단한 형태로 상호 배제를 사용하는 방법은 한번에 하나의 애플리케이션 의 인스턴스만 동작하독 하는것이다.
- 상다히 다양한 경우에 이런 방법이 필요하다.
- 프로그램이 동작하는 동안 특정 자원에 대해서 배타적으로 접근 하기를 원하거나 시스템 부하를 제한하고 싶을때 또는 프로그래므이 다수의 인스턴스가 동시에 동작하면 안되는 경우가 이에 해당한다.
- 애플리케이션이 하나만 동작하도록 하는것은 뮤텍스를 사용하면 간단히 구현할 수 있다.
'SYSTEM PROGRAMMING' 카테고리의 다른 글
IP 주소로의 여행 (0) | 2015.08.11 |
---|---|
경계적 대기 - win32 multihthreading (0) | 2015.08.11 |
SHELL PROGRAMMING (0) | 2015.08.10 |
GCC Compiiler - 02. 알아 두면 유용한 C소스 컴파일 과정 (0) | 2015.08.06 |
GCC Compiiler - 01. 컴파일의 이해 (0) | 2015.08.06 |