#ifndef __ZCPPMAIN__PROCESS_WIN_H__ #define __ZCPPMAIN__PROCESS_WIN_H__ #include "ZCppMain/ZMainHead.H" #include #include #include namespace ZNsMain { namespace ZNsEnum { /* In WinNT : #define STATUS_WAIT_0 ((DWORD )0x00000000L) In WinBase : #define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 ) 쓰레드 관련 함수가 에러일 경우에는 정책에 따라, ZEThread_Invalid 이외의 값을 반환할 수도 있다. */ enum ZEThread { ZEThread_OK = 0, // =WAIT_OBJECT_0 ZEThread_Invalid =-1, // 쓰레드 관련 함수 에러. ZEThread_TimeOut = WAIT_TIMEOUT };/* enum ZEThread*/ /*################################################## ■ barrier 의 대표적인 반환값. ■ barrier 에러를 체크할 때 BarrierClass VO_BarrierClass; if(VO_BarrierClass.Init()==ZEBarrier_NO) { // some code } 로 하지 말고. if(VO_BarrierClass.Init()!=ZEBarrier_OK) { // some code } 로 해야 안전하다. ##################################################*/ enum ZEBarrier { ZEBarrier_NO = false , ZEBarrier_OK = true };/* enum ZEBarrier*/ }/* namespace ZNsEnum*/ class ZCProcess { protected: STARTUPINFO mo_SI; PROCESS_INFORMATION mo_PI; public : bool Exec(LPCTSTR AP_ExeName, LPCTSTR APC_WorkDir=NULL, bool AB_Inherit=TRUE) { ::GetStartupInfoA(&mo_SI); return ::CreateProcessA( NULL , (LPSTR)AP_ExeName, // Name of app to launch NULL , // Default process security attributes NULL , // Default thread security attributes AB_Inherit , // Inherit handles from the parent 0 , // Normal priority NULL , // Use the same environment as the parent APC_WorkDir , // Launch in the current directory &mo_SI , // Startup Information &mo_PI // Process information stored upon return /*/////////*/ ) == TRUE ; }/* bool Exec(LPCTSTR AP_ExeName, LPCTSTR APC_WorkDir=NULL, bool AB_Inherit=TRUE)*/ HANDLE GetNowProcessHandle() { return ::GetCurrentProcess(); }/* HANDLE GetNowProcessHandle()*/ static long GetPID() { return ::GetCurrentProcessId(); // DWORD GetCurrentProcessId(VOID); }/* static long GetPID()*/ STARTUPINFO& GetStartUpInfo() { return mo_SI; }/* STARTUPINFO& GetStartUpInfo()*/ PROCESS_INFORMATION& GetProcInfo() { return mo_PI; }/* PROCESS_INFORMATION& GetProcInfo()*/ public: };/* class ZCProcess*/ ////////////////////////////////////////// /////////// end class ZCProcess /////////// ////////////////////////////////////////// // Memory Map class class ZCMemMap { private: HANDLE mh_MemMap; public : ZCMemMap() { mh_MemMap=NULL; }/* ZCMemMap()*/ /*//////////////////////////////////////////////////////////////////////////// ZTypLong ALL_Size 가 0 이면 AH_File 에서 지정한 파일의 크기가 그대로 사용된다. 단 AH_File 이 비어 있는 파일의 경우 ALL_Size 는 1 이상의 정수를 지정해야 한 다. AH_File 이 유효한 파일이고 ALL_Size 가 1 이상의 정수이면 AH_File 파일의 크기가 ALL_Size 로 즉시 변경된다. ////////////////////////////////////////////////////////////////////////////*/ bool Create(HANDLE AH_File=INVALID_HANDLE_VALUE, ZTypLong ALL_Size=0, DWORD ADW_ProtectFlag=PAGE_READWRITE, ::LPSECURITY_ATTRIBUTES AP_FileMappingAttributes=NULL, LPCTSTR AP_Name=NULL) { ::LARGE_INTEGER LI; LI.QuadPart=ALL_Size; return (mh_MemMap=::CreateFileMapping( AH_File, AP_FileMappingAttributes, ADW_ProtectFlag, LI.HighPart, LI.LowPart, AP_Name))!=NULL; }/* bool Create(HANDLE AH_File=INVALID_HANDLE_VALUE, ZTypLong ALL_Size=0, DWORD ADW_ProtectFlag=PAGE_READWRITE, ::LPSECURITY_ATTRIBUTES AP_FileMappingAttributes=NULL, LPCTSTR AP_Name=NULL)*/ /* LPVOID AP_BaseAddress 를 지정한다면 system's memory allocation granularity 의 배수여야 한다. granularity 를 알기 위해서는 GetSystemInfo 를 사용한다. VOID GetSystemInfo(LPSYSTEM_INFO lpSystemInfo) 참고. 이 값이 NULL 이면 시스템이 알아서 정한다. */ /* DWORD ADW_MapSize 가 0 이면 파일전체가 map 된다. */ LPVOID LinkMap(ZTypLong ALL_Offset=0, DWORD ADW_MapSize=0, DWORD ADW_DesiredAccess=FILE_MAP_ALL_ACCESS, LPVOID AP_BaseAddress=NULL) const { ::LARGE_INTEGER LI; LI.QuadPart=ALL_Offset; return ::MapViewOfFileEx( mh_MemMap, ADW_DesiredAccess, LI.HighPart, LI.LowPart, ADW_MapSize, AP_BaseAddress); }/* LPVOID LinkMap(ZTypLong ALL_Offset=0, DWORD ADW_MapSize=0, DWORD ADW_DesiredAccess=FILE_MAP_ALL_ACCESS, LPVOID AP_BaseAddress=NULL) const*/ bool UnMap(LPCVOID AP_BaseAddress) const { return ::UnmapViewOfFile((void*)AP_BaseAddress)==TRUE; }/* bool UnMap(LPCVOID AP_BaseAddress) const*/ /* cygwin 이나 mingw 에서는 이상하게 ::UnmapViewOfFile(void*) 로 정의되어 있다. 그래서 UnMap() 에서 인수를 (void*)AP_BaseAddress 으로 형변환해주고 있다. */ bool Close() { const bool CB_IsOK = (::CloseHandle(mh_MemMap)==TRUE); mh_MemMap=NULL; return CB_IsOK; }/* bool Close()*/ public: };/* class ZCMemMap*/ // 공유메모리 클래스 class ZCShareMemory { private: ZTypeID mh_FileMap; void* mp_Address; public : ZCShareMemory() { mh_FileMap=0; mp_Address=0; }/* ZCShareMemory()*/ ZTypeID GetID() const { return mh_FileMap; }/* ZTypeID GetID() const*/ bool IsValidID() const { return mh_FileMap!=0; }/* bool IsValidID() const*/ void* GetStartAddress() const { return mp_Address; }/* void* GetStartAddress() const*/ // Create() 멤버를 호출한 다음에는 LinkMap() 멤버를 호출해야 한다. bool Create(LPCTSTR AP_MapName, long AL_MapSize) { return (mh_FileMap = ::CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, AL_MapSize, AP_MapName))!=NULL ; /*////////////////////////////////////////////////////// HANDLE CreateFileMapping ( HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCTSTR lpName ); lpName The name of the file mapping object. If this parameter matches the name of an existing mapping object, the function requests access to the object with the protection that flProtect specifies. If this parameter is NULL, the file mapping object is created without a name. If lpName matches the name of an existing event, semaphore, mutex, waitable timer, or job object, the function fails, and the GetLastError function returns ERROR_INVALID_HANDLE. This occurs because these objects share the same namespace. -- lpName 과 같은 이름을 event, semaphore, mutex, waitable timer 등에서 -- 사용하고 있다면 GetLastError 함수가 ERROR_INVALID_HANDLE 을 반환한다. -- NULL 로 지정할 수 있다. Return Value If the function succeeds, the return value is a handle to the file mapping object. If the object exists before the function call, the function returns a handle to the existing object (with its current size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS. If the function fails, the return value is NULL. To get extended error information, call GetLastError. //////////////////////////////////////////////////////*/ }/* bool Create(LPCTSTR AP_MapName, long AL_MapSize)*/ bool Create(long AL_MapSize) { return this->Create(NULL/*lpMapName*/, AL_MapSize); }/* bool Create(long AL_MapSize)*/ /*/////////////////////////////////////////////////////////////////////////////// LPVOID MapViewOfFile ( HANDLE hFileMappingObject , DWORD dwDesiredAccess , DWORD dwFileOffsetHigh , DWORD dwFileOffsetLow , DWORD dwNumberOfBytesToMap ); hFileMappingObject FILE_MAP_ALL_ACCESS Equivalent to FILE_MAP_WRITE and FILE_MAP_READ. The object must have been created with the PAGE_READWRITE option. FILE_MAP_COPY A copy-on-write view of the file is mapped. The object must have been created with the PAGE_WRITECOPY option. If the file mapping object is backed by the operating system paging file, the system commits physical storage from the paging file at the time that MapViewOfFile is called. The actual physical storage is not used until a thread in the process writes to an address in the view. At that time, the system copies the original page to a new page that is backed by the paging file, maps the page into the process address space, and changes the page protection to PAGE_READWRITE. The threads in the process can access only the local copy of the data, not the original data. If the page is ever trimmed from the working set of the process, it can be written to the paging file storage that is committed when MapViewOfFile is called. This process only allocates physical memory when a virtual address is actually written to. Changes are never written back to the original file, and are freed when the thread in your process unmaps the view. Paging file space for the entire view is committed when copy-on-write access is specified, because the thread in the process can write to every single page. Therefore, enough physical storage space must be obtained at the time MapViewOfFile is called. FILE_MAP_EXECUTE An executable view of the file is mapped (mapped memory can be run as code). The object must have been created with the PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_READ option. Windows Server 2003 and Windows XP: This value is available starting with Windows XP SP2 and Windows Server 2003 SP1. Windows 2000: This value is not supported. -- FILE_MAP_EXECUTE 이 값이 정확히 무엇을 뜻하는지 잘 모르겠다. -- 코드로서 실행될 수 있다는 것 같은데, 그 코드의 실행은 어떻게 하는 것인가. -- 2008-02-08 21:48:00 FILE_MAP_READ A read-only view of the file is mapped. The object must have been created with the PAGE_READWRITE or PAGE_READONLY option. FILE_MAP_WRITE A read/write view of the file is mapped. The object must have been created with the PAGE_READWRITE option. dwFileOffsetHigh A high-order DWORD of the file offset where the view begins. dwFileOffsetLow A low-order DWORD of the file offset where the view is to begin. The combination of the high and low offsets must specify an offset within the file mapping. They must also match the memory allocation granularity of the system. That is, the offset must be a multiple of the allocation granularity. To obtain the memory allocation granularity of the system, use the GetSystemInfo function, which fills in the members of a SYSTEM_INFO structure. dwNumberOfBytesToMap The number of bytes of a file mapping to map to the view. All bytes must be within the maximum size specified by CreateFileMapping. If this parameter is 0 (zero), the mapping extends from the specified offset to the end of the file mapping. ///////////////////////////////////////////////////////////////////////////////*/ bool LinkMap(DWORD dwDesiredAccess=FILE_MAP_ALL_ACCESS, DWORD dwNumberOfBytesToMap=0) { return (mp_Address=::MapViewOfFile(mh_FileMap, dwDesiredAccess, 0, 0, dwNumberOfBytesToMap))!=0 ; }/* bool LinkMap(DWORD dwDesiredAccess=FILE_MAP_ALL_ACCESS, DWORD dwNumberOfBytesToMap=0)*/ bool UnMap() { bool VB_IsOK = (::UnmapViewOfFile(mp_Address)==TRUE) ; mp_Address=0; return VB_IsOK; }/* bool UnMap()*/ bool Close() { bool VB_IsOK = (::CloseHandle(mh_FileMap)==TRUE); mh_FileMap=0; return VB_IsOK; }/* bool Close()*/ public: };/* class ZCShareMemory*/ /////////////////////////////////////////////// /////////// end class ZCShareMemory /////////// /////////////////////////////////////////////// /*////////////////////////////////////////////////////////////////////////////// ■ class ZCProcessMutex 는 프로세스의 뮤텍스. Window 에서는 이름있는 mutex 를 쓰면 간단하지만 Linux 에서는 세마포어를 쓰는 것도 방법이다. 물론 pthread 라이브러리를 써서 공유메모리에 mutex 데이타를 넣 어도 되지만 다소 복잡하다. Linux 에서는 다중쓰레드에서 쓰는 세마포어와 다중 프로세스에서 쓰는 세마포어가 다르다. 정확히는 pthread 가 프로세스 mutex 는 아 직 지원하지 않는다. (2006-12-28 16:02:00) window 뮤텍스에는 '소유' 라는 개념이 있어서 소유한 뮤텍스가 해제를 해야 한다. 소유하지 않은 뮤텍스가 ReleaseMutex(뮤텍스) 를 실행하면 FALSE 를 반환한다. 또 한 해당 뮤텍스를 소유한 쓰레드는 그 뮤텍스에 아무리 락을 걸어도 내부 참조 카 운트만 올라갈 뿐 블럭되지 않는다. 또 그 카운트만큼 락을 해제해 주어야 한다. //////////////////////////////////////////////////////////////////////////////*/ class ZCProcessMutex { private: ZTypeID mh_Mutex; public : ZCProcessMutex() { mh_Mutex=0; }/* ZCProcessMutex()*/ ZTypeID GetHandle() const { return mh_Mutex; }/* ZTypeID GetHandle() const*/ bool Make(LPCTSTR AP_MutexName=NULL, LPSECURITY_ATTRIBUTES lpMutexAttributes=NULL, BOOL bInitialOwner=FALSE) { if(lpMutexAttributes==NULL) { // Mutex 핸들을 차일드 프로세스로 상속할 수 있게 한다. SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES_Obj; SECURITY_ATTRIBUTES_Obj.bInheritHandle =TRUE; SECURITY_ATTRIBUTES_Obj.lpSecurityDescriptor=NULL; SECURITY_ATTRIBUTES_Obj.nLength =sizeof(SECURITY_ATTRIBUTES); return (mh_Mutex = ::CreateMutex(&SECURITY_ATTRIBUTES_Obj, bInitialOwner, AP_MutexName))!=NULL ; }/* if(lpMutexAttributes==NULL)*/ return (mh_Mutex = ::CreateMutex(lpMutexAttributes, bInitialOwner, AP_MutexName))!=NULL ; }/* bool Make(LPCTSTR AP_MutexName=NULL, LPSECURITY_ATTRIBUTES lpMutexAttributes=NULL, BOOL bInitialOwner=FALSE)*/ bool Lock(DWORD ADW_TimeOut=INFINITE) { DWORD VI_Return=::WaitForSingleObject(mh_Mutex, ADW_TimeOut); return VI_Return!=WAIT_ABANDONED && VI_Return!=WAIT_FAILED ; /*/////////////////////////////////////////////////////////////////////////// ■ WaitForSingleObject() 함수는 해당 뮤텍스가 신호상태가 아니면 신호상태로 만들어주고 바로 리턴하지만, 비신호상태이면 신호상태가 될 때까지 대기한다. ■ 한 스레드가 뮤택스를 소유하면 이 뮤텍스를 기다리는 다른 스레드들은 모두 블럭 상태가 된다. 그런데 뮤텍스를 소유한 스레드가 어떠한 이유로 뮤텍스의 소유를 풀지 못하게 되면 포기된 뮤텍스(Abandoned Mutex) 라고 불린다. WaitForSingleObject() 함수의 리턴값으로 WAIT_ABANDONED 값을 전달받는 경우이며 이 포기된 뮤텍스를 어떻게 처리할 것인가는 프로그래머에게 달린 문제이다. 이것은 Critical Section 에 비해 약간의 안전장치가 될 수 있다. ///////////////////////////////////////////////////////////////////////////*/ }/* bool Lock(DWORD ADW_TimeOut=INFINITE)*/ int LockRaw(DWORD ADW_TimeOut=INFINITE) { return ::WaitForSingleObject(mh_Mutex, ADW_TimeOut); }/* int LockRaw(DWORD ADW_TimeOut=INFINITE)*/ int LockTime(DWORD AI_TimeOutMili) { return LockRaw(AI_TimeOutMili); }/* int LockTime(DWORD AI_TimeOutMili)*/ bool UnLock() { return ::ReleaseMutex(mh_Mutex)==TRUE ; }/* bool UnLock()*/ bool Close() { bool VB_Result=(::CloseHandle(mh_Mutex)==TRUE) ; mh_Mutex=0; return VB_Result; }/* bool Close()*/ public: };/* class ZCProcessMutex*/ /////////////////////////////////////////////// /////////// end class ZCProcessMutex /////////// /////////////////////////////////////////////// class ZCProcessMutexEasy : protected ZCProcessMutex { public: ZCProcessMutexEasy(LPCTSTR AP_MutexName) { if(this->ZCProcessMutex::Make(AP_MutexName)==false) { std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app); fileout<ZCProcessMutex::Make(AP_MutexName)==false)*/ }/* ZCProcessMutexEasy(LPCTSTR AP_MutexName)*/ ZCProcessMutexEasy() { if(this->ZCProcessMutex::Make()==false) { std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app); fileout<ZCProcessMutex::Make()==false)*/ }/* ZCProcessMutexEasy()*/ /*////////////////////////////////////////////////////////// ■ 아래 선언 줄에 ZCProcessMutex() 이 붙은 것에 대해서는 CProcess_Linux.H 의 class ZZCThreadMutexEasy 의 주석을 참고. -- 2013-05-05 07:05:00 //////////////////////////////////////////////////////////*/ ZCProcessMutexEasy(const ZCProcessMutexEasy& rhs) : ZCProcessMutex() { if(this->ZCProcessMutex::Make()==false) { std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app); fileout<ZCProcessMutex::Make()==false)*/ }/* ZCProcessMutexEasy(const ZCProcessMutexEasy& rhs)*/ ~ZCProcessMutexEasy() { if(this->ZCProcessMutex::Close()==false) { std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app); fileout<ZCProcessMutex::Close()==false)*/ }/* ~ZCProcessMutexEasy()*/ using ZCProcessMutex::Lock ; using ZCProcessMutex::UnLock; public: };/* class ZCProcessMutexEasy*/ typedef ZCProcessMutexEasy ZZCThreadMutexEasy; /*///////////////////////////////////////////////////////////// ■ class ZCProcessSemaphore 는 프로세스만을 위한 동기화 object. 리눅스에서는 프로세스 동기화에 Semaphore 만 쓸 수 있고 Window 에서는 프로세스 동기화에 Mutex 도 쓸 수 있다. 리눅스에서는 프로세스 동기화에 쓰이는 Semaphore 와 스레드 동기화에 쓰이는 세마포어가 함수와 용법이 다르다. ■ 리눅스의 class ZCThreadSemaphore 는 윈도우와 상당히 호환되기 어렵다. 리눅스와 호환을 위해서라면 class ZCProcessSemaphore 를 쓴다. 윈도우에서 이름있는 semaphore 는 프로세스에 전역적 이다. ■ ReleaseSemaphore() 함수는 해제하는 자원의 갯수를 2 이상으로 지정하더라도 블럭되고 있는 대기 쓰레드가 2 개 이상 깨어나는 것이 아니다. 반드시 현재 쓰레드에서 자신이 잠근 만큼 해제해 주어야 한다. 따라서 세마포어로 Posix 의 barrier 를 정확히 구 현할 수 없다. /////////////////////////////////////////////////////////////*/ class ZCProcessSemaphore { private: ZTypeID mh_Semaphore; public : ZCProcessSemaphore() { mh_Semaphore=0; }/* ZCProcessSemaphore()*/ ~ZCProcessSemaphore() { Close(); }/* ZCProcessSemaphore*/ bool Make(LPCSTR AP_SemaName, LONG AL_InitialCnt, LONG AL_MaximumCount, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes=NULL) { /*///////////////////////////////////////////////////////////////// ■ cf) CreateSemaphore() Return Values If the function succeeds, the return value is a handle to the semaphore object. If the named semaphore object existed before the function call, the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS. If the function fails, the return value is NULL. To get extended error information, call GetLastError. /////////////////////////////////////////////////////////////////*/ return (mh_Semaphore = ::CreateSemaphoreA( AP_SemaphoreAttributes, AL_InitialCnt, AL_MaximumCount, AP_SemaName))!=(HANDLE)ERROR_INVALID_HANDLE ; }/* bool Make(LPCSTR AP_SemaName,LONG AL_InitialCnt, LONG AL_MaximumCount, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes=NULL)*/ bool Make(LPCSTR AP_SemaName, LONG AL_InitialCnt, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes=NULL) { return (mh_Semaphore = ::CreateSemaphoreA( AP_SemaphoreAttributes, AL_InitialCnt, AL_InitialCnt, AP_SemaName))!=(HANDLE)ERROR_INVALID_HANDLE ; }/* bool Make(LPCSTR AP_SemaName, LONG AL_InitialCnt, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes=NULL)*/ // 아래 멤버는 뮤텍스처럼 쓰려는 경우에 사용한다. bool Make(LONG AL_InitialCnt=1, LONG AL_MaxCnt=1, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes=NULL) { return (mh_Semaphore=::CreateSemaphore( AP_SemaphoreAttributes, AL_InitialCnt, AL_MaxCnt, this->GetUniqueSemaKey()) )!=(HANDLE)ERROR_INVALID_HANDLE ; }/* bool Make(LONG AL_InitialCnt=1, LONG AL_MaxCnt=1, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes=NULL)*/ bool MakeZero(LPCSTR AP_SemaName=NULL) // 비신호상태의 세마포어를 만든다. { const int CI_MaxNumSemaphore=2100000000; return (mh_Semaphore=::CreateSemaphoreA( NULL, 0, CI_MaxNumSemaphore, AP_SemaName))!=(HANDLE)ERROR_INVALID_HANDLE ; }/* bool MakeZero(LPCSTR AP_SemaName=NULL)*/ static LPCSTR GetUniqueSemaKey() { static long SL_UniqueID=0 ; const long CL_AddID =100000; long VL_UniqueLong = ( SL_UniqueID += CL_AddID ) + ::GetCurrentProcessId() ; static char VCA_Buff[100]; ::wsprintf(VCA_Buff, _T("SemaKey%ld"), VL_UniqueLong); return VCA_Buff; }/* static LPCSTR GetUniqueSemaKey()*/ /* bool MakeStd() 는 class ZCProcessSemaphore 가 Window, Linux 양쪽에서 호환하기 위한 것 */ bool MakeStd(LPCSTR AP_SemaName, LONG AL_InitialCnt,LONG AL_MaximumCount) { return this->Make(AP_SemaName, AL_InitialCnt, AL_MaximumCount); }/* bool MakeStd(LPCSTR AP_SemaName, LONG AL_InitialCnt, LONG AL_MaximumCount)*/ bool Open(LPCSTR AP_SemaName, DWORD dwDesireAccess=SEMAPHORE_ALL_ACCESS, BOOL bInheritHandle=TRUE) { return (mh_Semaphore=::OpenSemaphore(dwDesireAccess, bInheritHandle, AP_SemaName))!=NULL ; }/* bool Open(LPCSTR AP_SemaName, DWORD dwDesireAccess=SEMAPHORE_ALL_ACCESS, BOOL bInheritHandle=TRUE)*/ /* 현재 세마포어의 값을 가져온다. 이 함수를 만든 작가 자신은 별로 자신이 없다고 말하고 있다. 가남사 김상형저 'API 정복' 1101 페이지 참고. 이 함수가 언제 필요하냐먄 세마포어에서 대기 중인 쓰레드 갯수를 모니터 링할 때 필요하다. -- 2015-09-16 00:40:00 */ static int GetValue(HANDLE AH_Sema) { if(::WaitForSingleObject(AH_Sema, 0)==WAIT_TIMEOUT) return 0; LONG VL_Count=0; ::ReleaseSemaphore(AH_Sema, 1, &VL_Count); return VL_Count+1; /*#####################################*/ }/* static int GetValue(HANDLE AH_Sema)*/ int GetValue() const { return GetValue(mh_Semaphore); }/* int GetValue() const*/ bool IsZeroCount() { return ::WaitForSingleObject(mh_Semaphore, 0)==WAIT_TIMEOUT; }/* bool IsZeroCount()*/ // cf) WaitForSingleObject() 은 시간초과의 경우에 WAIT_TIMEOUT 을 리턴한다. bool Lock(DWORD ADW_TimeOut=INFINITE) { return ::WaitForSingleObject(mh_Semaphore, ADW_TimeOut)!=WAIT_FAILED; }/* bool Lock(DWORD ADW_TimeOut=INFINITE)*/ /* LockRaw() 는 성공이면 ZEThread_OK, 시간초과는 ZEThread_TimeOut 을 반환한다. 리눅스의 Posix 세마포어에서도 같다. */ int LockRaw(DWORD ADW_TimeOut=INFINITE) { return ::WaitForSingleObject(mh_Semaphore, ADW_TimeOut); }/* int LockRaw(DWORD ADW_TimeOut=INFINITE)*/ int LockTime(DWORD AI_TimeOutMili) { return LockRaw(AI_TimeOutMili); }/* int LockTime(DWORD AI_TimeOutMili)*/ bool UnLock(LONG lReleaseCount=1, LPLONG lpPreviousCount=NULL) { /* cf) lReleaseCount [in] Amount by which the semaphore object's current count is to be increased. The value must be greater than zero. If the specified amount would cause the semaphore's count to exceed the maximum count that was specified when the semaphore was created, the count is not changed and the function returns FALSE. */ return ::ReleaseSemaphore(mh_Semaphore, lReleaseCount, lpPreviousCount)!=0 ; }/* bool UnLock(LONG lReleaseCount=1, LPLONG lpPreviousCount=NULL)*/ bool Close() { if(mh_Semaphore==0) return true; const bool CB_IsOK =( ::CloseHandle(mh_Semaphore)==TRUE) ; mh_Semaphore=0; return CB_IsOK; }/* bool Close()*/ public: };/* class ZCProcessSemaphore*/ //////////////////////////////////////////////// /////////// end class ZCProcessMutex /////////// //////////////////////////////////////////////// /*////////////////////////////////////////////// ■ 윈도우에서는 스레드용 세마포어와 프로세스용 세마포어를 구별할 필요가 없다. ■ 같은 쓰레드에서 ::EnterCriticalSection() 의 중복 호출은 블럭되지 않는다. 이 점은 Mutex 와 같다. //////////////////////////////////////////////*/ typedef ZCProcessSemaphore ZCThreadSemaphore; typedef ZCProcessMutex ZCThreadMutex ; typedef ZCProcessMutex ZCThreadMutexStd ; // posix 에서는 spin lock 으로 구현한다. class ZCCriticSect // Critical Section { private: CRITICAL_SECTION mo_CriticSect; public : void Init() { ::InitializeCriticalSection(&mo_CriticSect); }/* void Init()*/ bool Init(DWORD AI_SpinCount) { return ::InitializeCriticalSectionAndSpinCount(&mo_CriticSect, AI_SpinCount)==TRUE; }/* bool Init(DWORD AI_SpinCount)*/ void Fini() { ::DeleteCriticalSection(&mo_CriticSect); }/* void Fini()*/ void Lock (){::EnterCriticalSection(&mo_CriticSect);} void UnLock(){::LeaveCriticalSection(&mo_CriticSect);} /*////////////////////////////////////////////////////////////////////////////////// ■ 이상하게 TryEnterCriticalSection 함수를 호출할 수 없다. -- 2007-05-17 14:34:00) bool TryLock() { return TryEnterCriticalSection(&mo_CriticSect)==TRUE; } /////////////////////////////////////////////////////////////////////////////////////*/ #if defined(__VISUAL_CPP_VER__) && __VISUAL_CPP_VER__>=200800 bool WaitCond(::PCONDITION_VARIABLE AP_Cond, DWORD AI_Mili=INFINITE) { return ::SleepConditionVariableCS(AP_Cond, &mo_CriticSect, AI_Mili)==TRUE; }/* bool WaitCond(::PCONDITION_VARIABLE AP_Cond, DWORD AI_Mili=INFINITE)*/ #endif //defined(__VISUAL_CPP_VER__) && __VISUAL_CPP_VER__>=200800 public: };/* class ZCCriticSect*/ class ZCCriticSectEasy : protected ZCCriticSect { public: ZCCriticSectEasy(){this->Init();} ZCCriticSectEasy(const ZCCriticSectEasy&){this->Init();} ~ZCCriticSectEasy(){this->Fini();} void Lock (){this->ZCCriticSect::Lock ();} void UnLock(){this->ZCCriticSect::UnLock();} public: };/* class ZCCriticSectEasy : public ZCCriticSect*/ typedef ZCCriticSectEasy ZCDefLockEasy ; typedef ZCCriticSectEasy ZCFastLockEasy; template class ZtCAutoKeyRev; /* ZtCAutoKey<> 템플릿은 Lock 을 설정하고 해제한다. 열고 닫을 수 있는 열쇠와 같아 Key 라고 하였다. 이런 식의 기법을 RAII(Resource Acquisition Is Initialization) 라 고 한다는 것을 알았다. -- 2015-03-10 15:08:00 */ template class ZtCAutoKey { public : template friend class ZtCAutoKeyRev; private: ZtCAutoKey(const ZtCAutoKey& rhs){} private: TCriticSectEasy& mr_SyncEasy; #ifdef _DEBUG_CAUTOKEY_ static int msi_CallCnt; #endif //_DEBUG_CAUTOKEY_ public: ZtCAutoKey(TCriticSectEasy& AR_SyncEasy):mr_SyncEasy(AR_SyncEasy) { #ifdef _DEBUG_CAUTOKEY_ cout<<"▶▶▶▶ ZtCAutoKey:: ZtCAutoKey() "< class ZtCAutoKey */ #ifdef _DEBUG_CAUTOKEY_ template int ZtCAutoKey::msi_CallCnt = 0; #endif //_DEBUG_CAUTOKEY_ /*//////////////////////////////////////////////////////////////// ■ ZtCAutoKeyRev<> 는 ZtCAutoKey<> 을 생성자에서 인수로 받아서 생성자에서 Lock 을 해제하고, 소멸자에서 다시 Lock 을 걸어 준다. 그런데 이렇게 해야 될 상황 자체를 만들지 말자. 억지로 사용하다 보면 자주 Dead Lock 상태에 빠진다. ※ 2008-04-09 21:01:00 ////////////////////////////////////////////////////////////////*/ template class ZtCAutoKeyRev { private: TAutoKey& mr_CAutoKey; public : ZtCAutoKeyRev(TAutoKey& AR_CAutoKey):mr_CAutoKey(AR_CAutoKey) { mr_CAutoKey.mr_SyncEasy.UnLock(); }/* ZtCAutoKeyRev(TAutoKey& AR_CAutoKey)*/ ~ZtCAutoKeyRev() { mr_CAutoKey.mr_SyncEasy.Lock(); }/* ~ZtCAutoKeyRev()*/ public: };/* template class ZtCAutoKeyRev */ /*//////////////////////////////////////////////////// ■ http://blog.naver.com/kimsk99?Redirect=Log&logNo=50004383787 윈도우 프로그램을 하면서 쓰레드를 생성할 때, CreateThread와 _beginthread라는 두 함수 중, 어떤 함수를 사용해야 할 지 고민하게 된다. 난 항상 뭔가 기본 API같은 CreateThread를 사용해 왔었다. 그런데 오늘 책을 읽다가 그게 잘못되었다는 것을 알았다. MSDN에 나온 설명을 자세히 읽어보면 나오는 내용이었지만 보통 자세히는 안 읽으니 그동안 몰랐었다. 일단 CreateThread는 윈도우 SDK에서 제공하는 기본 API 이다. 특별한 라이브러리와 링크하지 않아도 윈도우라면 컴파일되고 돌아간다. 하지만 _beginthread(또는 _beginthreadex)는 standard C library에 속한 함수로 링크해야 동작한다. 그렇기 때문에 기능에서도 차이가 난다. 사실 기능에서 차이가 나지 않는다면 이렇게 구별할 이 유가 없었을 것이다. _beginthread와 같은 경우 인자의 종류가 달라서 쓰레드 생성시 필요한 설정을 할 수 없는 경우가 있다. 그러나 비슷 한 함수인 _beginthreadex는 완전히 CreateThread와 같은 인자를 가지고 있다. 그렇기 때문에 CreateThread 가 쓰인 모든 곳은 _beginthreadex 로 치환이 가능하다. 내부동작에서 가장 큰 차이점은 _beginthread 는 standard C library에서 사용하는 영역에 대한 TLB 초기화를 해준다는 것이다. standary C library에 있는 함수 중 일부는 thread-safty를 위해서 TLB를 사용하는데 초기화가 되지 않는다면 문제가 발생할 수 있다. _beginthreadex 함수를 사용해서 쓰레드를 구동했다면 스레드 함수 내에서 쓰레드를 종료하기 위해서 ExitThread 함수를 사용하기 보다는 _endthreadex 함수를 사용하길 권장한다. 대부분의 경우 C library를 사용하기 때문에 쓰레드의 생성은 _beginthreadex 를 사용해야 한다. uintptr_t _beginthreadex ( void *security, unsigned stack_size, unsigned ( *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr ); ■ -- ////////////////////////////////////////////////////*/ ////////////////////////////////////////////////////// ///////////////// 쓰레드 관련 클래스 ///////////////// ////////////////////////////////////////////////////// /*//////////////////////////////////////////////////// uintptr_t _beginthreadex ( void *security, unsigned stack_size, unsigned ( __stdcall *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr ); HANDLE CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE AP_StartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); CreateThread 는 _beginthreadex 로 대체될 수 있고 그래야 한다. dwCreationFlags [in] Flags that control the creation of the thread. If the CREATE_SUSPENDED flag is specified, the thread is created in a suspended state, and will not run until the ResumeThread function is called. If this value is zero, the thread runs immediately after creation. If the STACK_SIZE_PARAM_IS_A_RESERVATION flag is specified, the dwStackSize parameter specifies the initial reserve size of the stack. Otherwise, dwStackSize specifies the commit size. Windows 2000/NT and Windows Me/98/95: The STACK_SIZE_PARAM_IS_A_RESERVATION flag is not supported. lpThreadId [out] Pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned. Windows Me/98/95: This parameter may not be NULL. cf) DWORD ResumeThread( HANDLE hThread ); **** IN WINBASE.H **** typedef DWORD (WINAPI *PTHREAD_START_ROUTINE) ( LPVOID lpThreadParameter ); typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE; LPTHREAD_START_ROUTINE AP_StartAddress 는 시작함수포인터 원형은 DWORD WINAPI ThreadFunc(LPVOID lpRarameter) ********** MFC 의 동기화 object ********** 아래 object 를 쓰기 위해서는 가 필요 1) CCriticalSection cs; cs.Lock(); // 작업코드 cs.Unlock(); 2) CEvent event; Thread 들이 참조할 수 있도록 전역 또는 상위 블럭에 CEvent object 를 선언하고 Thread CallBack 함수에서 UINT ThreadFunc(LPVOID pParam) { while(TRUE) { CEventOvj.Lock(); // 잠이든다. // 이벤트가 발생할 때 실행될 코드를 적는다. // 그리고 외부에서 // CEventOvj.PulseEvent(); 이나 // CEventOvj.SetEvent(); 를 호출하면 깨어난다. // Thread 가 Lock 이 걸려 있지 않은 상태에서 // PulseEvent 함수를 먼저 호출하면 아무일도 일어나지 않고 // 바로 다음 순간에 Lock() 를 호출하면 다시 잠을 잔다. // SetEvent() 의 경우에는 그냥 통과한다. } //while(TRUE) } //UINT ThreadFunc(LPVOID pParam) # Window Thread Callback Type; DWORD WINAPI ThreadFunc(LPVOID lpParameter); cf) typedef unsigned long DWORD; # Linux Thread Callback Type: void* ThreadFunc()(void*) ////////////////////////////////////////////////////*/ typedef DWORD ThreadReturnType ; typedef HANDLE ThreadID ; /*/////////////////////////////////////////////////////////////////// typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; ///////////////////////////////////////////////////////////////////*/ #define _THREAD_RETURN_ unsigned __stdcall namespace ZNsIFace { class ZCThread_BASE{}; }/* namespace ZNsIFace*/ template< typename TTypeBase=ZNsIFace::ZCThread_BASE > class ZtCThread : public TTypeBase /////////////////// { protected: ThreadID mh_ThreadID; public : ZtCThread() { _DEBUG_REENTRANT_CHECK_ mh_ThreadID = INVALID_HANDLE_VALUE ; }/* ZtCThread()*/ ZtCThread(TTypeBase& AR_CBaseType):TTypeBase(AR_CBaseType) { _DEBUG_REENTRANT_CHECK_ }/* ZtCThread(TTypeBase& AR_CBaseType):TTypeBase(AR_CBaseType)*/ ZtCThread(const ZtCThread& rhs):TTypeBase(rhs) { _DEBUG_REENTRANT_CHECK_ mh_ThreadID =rhs.mh_ThreadID ; (TTypeBase&)(*this)=static_cast(rhs); }/* ZtCThread(TTypeBase& AR_CBaseType):TTypeBase(AR_CBaseType)*/ template ZtCThread(const TTypeArg& AR_TTypeArg):TTypeBase(AR_TTypeArg) { _DEBUG_REENTRANT_CHECK_ }/* template ZtCThread(const TTypeArg& AR_TTypeArg):TTypeBase(AR_TTypeArg) */ ZtCThread& operator=(const ZtCThread& rhs) { return *this; // nothing to do }/* ZtCThread& operator=(const ZtCThread& rhs)*/ operator ThreadID () const { return mh_ThreadID; }/* operator ThreadID () const*/ ThreadID GetThreadID() const { return mh_ThreadID; }/* ThreadID GetThreadID() const*/ /*/////////////////////////////////////////////////// typedef DWORD (WINAPI *PTHREAD_START_ROUTINE) ( LPVOID lpThreadParameter ); typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE; ///////////////////////////////////////////////////*/ bool Make(LPTHREAD_START_ROUTINE AP_StartAddress, void* AP_Arg=0, DWORD dwCreationFlags=0, DWORD dwStackSize=0, LPSECURITY_ATTRIBUTES lpThreadAttributes=NULL) { unsigned int VUI_ThreadID; return ( mh_ThreadID = (void*)::/*CreateThread*/_beginthreadex ( lpThreadAttributes , dwStackSize , (unsigned int (__stdcall *)(void *))AP_StartAddress , AP_Arg , dwCreationFlags , &VUI_ThreadID ) /*****/ )!=INVALID_HANDLE_VALUE ; ////////////////////////////////// #if 0 // CreateThread() 함수를 쓰는 경우 DWORD VUI_ThreadID; return ( mh_ThreadID=::CreateThread ( lpThreadAttributes, dwStackSize, AP_StartAddress, AP_Arg, dwCreationFlags, &VUI_ThreadID ) /*****/ )!=INVALID_HANDLE_VALUE ; #endif }/* bool Make(LPTHREAD_START_ROUTINE AP_StartAddress, void* AP_Arg=0, DWORD dwCreationFlags=0, DWORD dwStackSize=0, LPSECURITY_ATTRIBUTES lpThreadAttributes=NULL)*/ bool Make(unsigned (__stdcall *AP_StartAddress)(void *), void* AP_Arg=0, DWORD dwCreationFlags=0, DWORD dwStackSize=0, LPSECURITY_ATTRIBUTES lpThreadAttributes=NULL) { unsigned int VUI_ThreadID; return ( mh_ThreadID=(void*)::_beginthreadex ( lpThreadAttributes , dwStackSize , AP_StartAddress , AP_Arg , dwCreationFlags , &VUI_ThreadID ) /*****/ )!=INVALID_HANDLE_VALUE ; ///////////// }/* bool Make(unsigned (__stdcall AP_StartAddress*)(void *), void* AP_Arg=0, DWORD dwCreationFlags=0, DWORD dwStackSize=0, LPSECURITY_ATTRIBUTES lpThreadAttributes=NULL)*/ // 쓰레드 종료시까지 메인 함수의 실행을 지연(블럭)시킨다. int Wait(DWORD ADW_MilliSeconds=INFINITE) { return ::WaitForSingleObject(mh_ThreadID, ADW_MilliSeconds); /* WaitForSingleObject(HANDLE,DWORD ADW_MilliSeconds); 는 3 종류의 반환값을 갖는다. WAIT_OBJECT_0 : HANDLE object 가 신호상태가 되었다. WAIT_TIMEOUT : 타임아웃시간이 경과하였다. WAIT_ABANDONED : 포기된 뮤텍스 */ }/* int Wait(DWORD ADW_MilliSeconds=INFINITE)*/ static int Wait(ThreadID AI_ThreadIDVar, DWORD ADW_MilliSeconds=INFINITE) { return ::WaitForSingleObject(AI_ThreadIDVar, ADW_MilliSeconds); /* WaitForSingleObject(HANDLE,DWORD ADW_MilliSeconds); 는 3 종류의 반환값을 갖는다. WAIT_OBJECT_0 : HANDLE object 가 신호상태가 되었다. WAIT_TIMEOUT : 타임아웃시간이 경과하였다. WAIT_ABANDONED : 포기된 뮤텍스 */ }/* static int Wait(ThreadID AI_ThreadIDVar, DWORD ADW_MilliSeconds=INFINITE)*/ bool Terminate(DWORD AI_ExitCode=0) { return ::TerminateThread(mh_ThreadID, AI_ExitCode)==TRUE; }/* bool Terminate(DWORD AI_ExitCode=0)*/ /*//////////////////////////////////////////////////////// BOOL GetExitCodeThread( HANDLE hThread, LPDWORD lpExitCode ); ■ Parameters □ hThread [in] Handle to the thread. □ lpExitCode [out] Pointer to a 32-bit variable to receive the thread termination status. ■ Return Values Nonzero indicates success. Zero indicates failure. To get extended error information, call GetLastError. ■ Remarks If the specified thread has not terminated, the termination status returned is STILL_ACTIVE. The following termination statuses can be returned if the process has terminated: The exit value specified in the ExitThread or TerminateThread function The return value from the thread function The exit value of the thread's process ////////////////////////////////////////////////////////*/ BOOL GetExitCode(DWORD& ARRDW_ExitCode) { return ::GetExitCodeThread(mh_ThreadID, &ARRDW_ExitCode); }/* BOOL GetExitCode(DWORD& ARRDW_ExitCode)*/ /*////////////////////////////////////////////////////////////// ■ SuspendThread 함수는 쓰레드의 동작을 중지시키고 ResumeThread 함수는 중지된 쓰레드를 동작시킨다. 윈도우 쓰레드는 내부적으로 중지카운터를 유지하는데 이 카운트는 SuspendThread 함수가 호출되면 증가하고 ResumeThread 함수가 호 출되면 감소하며 이 카운트가 0 이면 쓰레드는 재개된다. 그래서 SuspendThread 함수를 두번 호출했다면 ResumeThread 함수도 두번 호출해 주어야 쓰레드가 재개된다. 그렇다면 ResumeThread 를 먼저 호출하고 SuspendThread 를 나중에 호출하면 어떻게 될까. 이 경우에는 블럭된다. 내부 카운트가 -1 에서 0 으로 되어 계속 진행할 것으로 예상하지만 내부 카운트는 0 미만으로는 셋팅이 되지 않는 것 같다. POSIX 쓰레드표준안에서는 이런 함수가 없는데, POSIX 쓰레드를 이 용한 프로그래밍 (정보문화사, David R. Butenhof) 301 Page 에 suspend 와 resume 를 구현한 예제가 나와있다. 솔라리스에서는 thr_suspend(), thr_continue() 함수를 지원한다. ■ -- //////////////////////////////////////////////////////////////*/ DWORD Suspend() { return ::SuspendThread(mh_ThreadID); }/* DWORD Suspend()*/ DWORD Resume() { return ::ResumeThread(mh_ThreadID); }/* DWORD Resume()*/ bool Close() { return ::CloseHandle(mh_ThreadID)==TRUE; }/* bool Close()*/ bool Detach() // for compatibility with linux { return true; }/* bool Detach()*/ public: };/* template< typename TTypeBase=ZNsIFace::ZCThread_BASE >/ class ZtCThread ////////////////////////////////////*/ template<> class ZtCThread { protected: ThreadID mh_ThreadID; public : ZtCThread() { _DEBUG_REENTRANT_CHECK_ mh_ThreadID = INVALID_HANDLE_VALUE ; }/* ZtCThread()*/ ZtCThread(const ZtCThread& rhs) { mh_ThreadID=rhs.mh_ThreadID; }/* ZtCThread(const ZtCThread& rhs)*/ ZtCThread& operator=(const ZtCThread& rhs) { return *this; }/* ZtCThread& operator=(const ZtCThread& rhs)*/ operator ThreadID () const { return mh_ThreadID; }/* operator ThreadID () const*/ ThreadID GetThreadID() const { return mh_ThreadID; }/* ThreadID GetThreadID() const*/ bool Make(LPTHREAD_START_ROUTINE AP_StartAddress, void* AP_Arg=0, DWORD dwCreationFlags=0, DWORD dwStackSize=0, LPSECURITY_ATTRIBUTES lpThreadAttributes=NULL) { unsigned int VUI_ThreadID; return ( mh_ThreadID = (void*)::_beginthreadex ( lpThreadAttributes, dwStackSize, (unsigned int (__stdcall *)(void *))AP_StartAddress, AP_Arg, dwCreationFlags, &VUI_ThreadID ) /*****/ )!=INVALID_HANDLE_VALUE ; /////////////// #if 0 // CreateThread() 함수를 쓰는 경우 DWORD VUI_ThreadID; return ( mh_ThreadID = ::CreateThread ( lpThreadAttributes, dwStackSize, AP_StartAddress, AP_Arg, dwCreationFlags, &VUI_ThreadID ) /*****/ )!=INVALID_HANDLE_VALUE ; #endif }/* bool Make(LPTHREAD_START_ROUTINE AP_StartAddress, void* AP_Arg=0, DWORD dwCreationFlags=0, DWORD dwStackSize=0, LPSECURITY_ATTRIBUTES lpThreadAttributes=NULL)*/ bool Make(unsigned (__stdcall *AP_StartAddress)(void *), void* AP_Arg=0, DWORD dwCreationFlags=0, DWORD dwStackSize=0, LPSECURITY_ATTRIBUTES lpThreadAttributes=NULL) { unsigned int VUI_ThreadID; return ( mh_ThreadID=(void*)::_beginthreadex ( lpThreadAttributes , dwStackSize , AP_StartAddress , AP_Arg , dwCreationFlags , &VUI_ThreadID ) /*****/ )!=INVALID_HANDLE_VALUE ; ///////////// }/* bool Make(unsigned (__stdcall AP_StartAddress*)(void *), void* AP_Arg=0, DWORD dwCreationFlags=0, DWORD dwStackSize=0, LPSECURITY_ATTRIBUTES lpThreadAttributes=NULL)*/ int Wait(DWORD ADW_MilliSeconds=INFINITE) { return ::WaitForSingleObject(mh_ThreadID, ADW_MilliSeconds); }/* int Wait(DWORD ADW_MilliSeconds=INFINITE)*/ static int Wait(ThreadID AI_ThreadIDVar, DWORD ADW_MilliSeconds=INFINITE) { return ::WaitForSingleObject(AI_ThreadIDVar, ADW_MilliSeconds); }/* static int Wait(ThreadID AI_ThreadIDVar, DWORD ADW_MilliSeconds=INFINITE)*/ bool Terminate(DWORD AI_ExitCode=0) { return ::TerminateThread(mh_ThreadID, AI_ExitCode)==TRUE; }/* bool Terminate(DWORD AI_ExitCode=0)*/ BOOL GetExitCode(DWORD& ARRDW_ExitCode) { return ::GetExitCodeThread(mh_ThreadID, &ARRDW_ExitCode); }/* BOOL GetExitCode(DWORD& ARRDW_ExitCode)*/ DWORD Suspend() { return ::SuspendThread(mh_ThreadID); }/* DWORD Suspend()*/ DWORD Resume() { return ::ResumeThread(mh_ThreadID); }/* DWORD Resume()*/ bool Close() { return ::CloseHandle(mh_ThreadID)==TRUE; }/* bool Close()*/ bool Detach() // for compatibility with linux { return true; }/* bool Detach()*/ public: };/* template<> class ZtCThread*/ /*/////////////////////////////////////////////////////////////////////////////// ■ 신호상태 : 쓰레드의 실행을 허가하는 상태 자동 리셋 이벤트 : 대기 상태가 종료되면 자동으로 비신호 상태가 된다. 수동 리셋 이벤트 : 쓰레드가 비신호 상태로 만들어 줄 때까지 신호상태를 유지한다. SetEvent 는 이벤트를 신호상태로 만들고, ResetEvent 는 이벤트를 비신호상태로 만든다. (즉 다른 쓰레드가 진입할 수 없는 상태로 만든다.) 자동 리셋 이벤트를 만든 쓰레드가 WaitForSingleObject() 를 호출하면 다른 쓰레드 가 SetEvent() 함수로 그 이벤트를 신호 상태로 만들 때까지 혹은 지정한 시간만큼 블럭된다. 이벤트는 다수의 쓰레드가 어떤 조건을 기다리다가 그 조건이 충족되었을 때 일제히 시작하게 하는 기능을 할 수 있는데 리눅스에서는 판독자/기록자 lock 이나 뮤텍스의 조건 변수로 구현할 수 있다. 물론 세마포어로도 할 수 있다. 다수의 쓰레드가 세마 포어에서 대기하고 있다가 어떤 해제 조건이 되면 세마포어 카운트를 쓰레드 갯수만 큼 늘려주면 모든 쓰레드가 깨어날 것이다. 세마포어를 사용하는 경우, (리눅스에서는 테스트해보지 않았지만) Window 에서는 단 순히 세마포어 카운트를 대기하는 쓰레드 갯수만큼 늘려주어도 모든 대기 쓰레드가 한번에 깨어나지 않았다. 세마포어 카운트를 1 증가시키는 코드를 대기 쓰레드 갯수 만큼 실행해 주어야 한다. ※ 2008-05-05 15:38:00 ///////////////////////////////////////////////////////////////////////////////*/ template class ZtCEvent : public TData { protected: ZTypeID mh_TypeID; public : /*////////////////////////////////////////////////////////////////////////////// ■ CreateEvent() A handle to the event object indicates success. If the named event object existed before the function call, the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS. NULL indicates failure. To get extended error information, call GetLastError. //////////////////////////////////////////////////////////////////////////////*/ bool Make(LPCTSTR AP_Name, BOOL AB_Reset=TRUE, BOOL AB_ManualReset=TRUE, LPSECURITY_ATTRIBUTES AP_SecAtt=NULL) { return (mh_TypeID = ::CreateEvent(AP_SecAtt, AB_Reset, AB_ManualReset, AP_Name))!=NULL; }/* bool Make(LPCTSTR AP_Name, BOOL AB_Reset=TRUE, BOOL AB_ManualReset=TRUE, LPSECURITY_ATTRIBUTES AP_SecAtt=NULL)*/ bool Set() const // 이벤트를 신호상태로 만든다. { return ::SetEvent(mh_TypeID)==TRUE; }/* bool Set() const*/ bool Reset() const // 이벤트를 비신호상태로 만든다. { return ::ResetEvent(mh_TypeID)==TRUE; }/* bool Reset()*/ bool Pulse() const { return ::PulseEvent(mh_TypeID)==TRUE; /*////////////////////////////////////////////////////////////////////////////////////////////////////// ■ 수동리셋 이벤트에서 SetEvent() 함수를 호출하여 이벤트를 신호 상태로 만든다. 그리고 대기하던 쓰레드가 대기 상태를 벗어나면 다시 자동으로 이벤트를 비신호 상태로 만들어준다. 그러니까 수동 리셋 이벤트에서 자동 리셋 이벤트 효과를 가져다 주는 함수이다. 그런데 MSDN 에서는 불안정하기 때문에 사용하지 말라고 되 어 있다. http://msdn.microsoft.com/en-us/library/ms684914(VS.85).aspx Sets the specified event object to the signaled state and then resets it to the nonsignaled state after releasing the appropriate number of waiting threads. Note : This function is unreliable and should not be used. It exists mainly for backward compatibility. For more information, see Remarks. ■ 위에서 정리하기를 "수동 리셋 이벤트에서 자동 리셋 이벤트 효과를 가져다 주는 함수이다." 라고 했는데 이것은 틀리다. 이벤트를 신호 상태로 만들고, 그 이벤트에서 대기하던 모든 쓰레드가 깨어난 것을 확인한 후에 그 이벤트를 다시 비신호 상태로 만든다. 따라서 이벤트를 신호 상태로 만든 다음, 그 이 벤트에서 대기하던 모든 쓰레드가 깨어날 때까지 일정 기간을 대기해야 한다. 아마 이 부분 처리가 불안정 하기 때문에 MSDN 에서 "unreliable" 이라고 했을 것이다. -- 2009-02-05 0:10:00 //////////////////////////////////////////////////////////////////////////////////////////////////////*/ }/* bool Pulse() const*/ int Wait(DWORD ADW_MilliSeconds=INFINITE) const { return ::WaitForSingleObject(mh_TypeID, ADW_MilliSeconds); }/* int Wait(DWORD ADW_MilliSeconds=INFINITE) const*/ /*////////////////////////////////////////////////////////////////////////////////////// ■ ::WaitForSingleObject() 의 반환값 WAIT_OBJECT_0 : The state of the specified object is signaled. WAIT_TIMEOUT : The time-out interval elapsed, and the object's state is nonsignaled. WAIT_FAILED : Fail 아래 Lock 계열 함수는 에러가 아니면 ZEThread_OK 나 ZEThread_TimeOut 을 반환할 수 있다. //////////////////////////////////////////////////////////////////////////////////////*/ int Lock() const { return ::WaitForSingleObject(mh_TypeID, INFINITE); }/* int Lock() const*/ int LockRaw(DWORD ADW_MilliSeconds=INFINITE) const { return ::WaitForSingleObject(mh_TypeID, ADW_MilliSeconds); }/* int LockRaw(DWORD ADW_MilliSeconds=INFINITE) const*/ int LockTime(DWORD AI_TimeOutMili) { return LockRaw(AI_TimeOutMili); }/* int LockTime(DWORD AI_TimeOutMili)*/ bool UnLock() const // 이벤트를 신호상태로 만든다. { return this->Set(); }/* bool UnLock()*/ public: };/* template class ZtCEvent */ template class ZtCBarrier : public TData { protected: ZTypeID mh_EventID; volatile LONG ml_BarrCnt; public : ZtCBarrier() { mh_EventID=0; ml_BarrCnt=0; }/* ZtCBarrier()*/ bool IsValid() const{return mh_EventID != 0 ; } /*///////////////////////////////////////////////////////////////////////////// ■ Init() 와 Fini() 의 반환값이 linux 와 호환을 위해 bool 이 아니고 int 형이다. 반환값을 다룰 때는 if(CBarrierObj.Init(3)==ZNsEnum::ZEBarrier_OK) 이런 식으로 하자. ■ Init() 함수는 비신호 상태인 수동 리셋 이벤트를 만든다. /////////////////////////////////////////////////////////////////////////////*/ int Init(unsigned AI_Count, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes=NULL, LPCTSTR AP_EventName=NULL) { if(AI_Count<1) AI_Count=1; ml_BarrCnt=AI_Count; /*//////////////////////////////////////////////////// ■ ::CreateEvent() 의 3 번 인수 : bInitialState If this parameter is TRUE, the initial state of the event object is signaled; otherwise, it is nonsignaled ////////////////////////////////////////////////////*/ if(mh_EventID!=0) return ::ResetEvent(mh_EventID)==TRUE; return (mh_EventID=::CreateEvent( ////////////////////// AP_SemaphoreAttributes , TRUE /*bManualReset */ , FALSE /*bInitialState*/ , AP_EventName) /*/////////*/ )!=(HANDLE)ERROR_INVALID_HANDLE ; //////// }/* int Init(unsigned AI_Count, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes=NULL, LPCTSTR AP_EventName=NULL)*/ int Fini() { bool VB_IsOK= (::CloseHandle(mh_EventID)==TRUE); mh_EventID=0; return VB_IsOK; }/* int Fini()*/ bool Wait() { if(::InterlockedDecrement(&ml_BarrCnt)>0) { return ::WaitForSingleObject(mh_EventID, INFINITE) != WAIT_FAILED; }/* if(::InterlockedDecrement(&ml_BarrCnt)>0)*/ return ::SetEvent(mh_EventID)==TRUE ; }/* bool Wait()*/ /*//////////////////////////////////////////////////////////////////////////////// ■ LONG __cdecl InterlockedDecrement(__inout LONG volatile *Addend); Parameters : Addend [in, out] : A pointer to the variable to be decremented. Return Value : The function returns the resulting decremented value. The variable pointed to by the Addend parameter must be aligned on a 32-bit boundary; otherwise, this function will behave unpredictably on multiprocessor x86 systems and any non-x86 systems. See _aligned_malloc. The interlocked functions provide a simple mechanism for synchronizing access to a variable that is shared by multiple threads. This function is atomic with respect to calls to other interlocked functions. This function is implemented using a compiler intrinsic where possible. For more information, see the Winbase.h header file and _InterlockedDecrement. This function generates a full memory barrier (or fence) to ensure that memory operations are completed in order. ■ Provides compiler intrinsic support for the Win32 Platform SDK InterlockedDecrement function. long _InterlockedDecrement( long * lpAddend ); long _InterlockedDecrement_acq( long * lpAddend ); long _InterlockedDecrement_acq( long * lpAddend ); short _InterlockedDecrement16( short * lpAddend ); short _InterlockedDecrement16_acq( short * lpAddend ); short _InterlockedDecrement16_rel( short * lpAddend ); __int64 _InterlockedDecrement64( __int64 * lpAddend ); __int64 _InterlockedDecrement64_acq( __int64 * lpAddend ); __int64 _InterlockedDecrement64_rel( __int64 * lpAddend ); Parameters [in, out] lpAddend Pointer to the variable to be decremented. Return Value Windows 98, Windows NT 4.0, and later: The return value is the resulting decremented value. Windows 95, Windows NT 3.51, and earlier: If the result of the operation is zero, the return value is zero. If the result of the operation is less than zero, the return value is negative, but it is not necessarily equal to the result. If the result of the operation is greater than zero, the return value is positive, but it is not necessarily equal to the result. ■ 위의 MSDN 정보로 판단하면 InterlockedDecrement 함수는 _InterlockedDecrement~ 계열의 SDK 를 사용하는데 이들 SDK 는 '꼭 감소된 값을 반환하지는 않는다'는 것을 알 수 있다. 이것은 함수 LONG __cdecl InterlockedIncrement(__inout LONG volatile *Addend); 에 대해서도 마찬가지이다. ////////////////////////////////////////////////////////////////////////////////*/ public: };/* template< typename TData=ZNsMain::ZCEmpty > class ZtCBarrier ////////////////////////*/ /*/////////////////////////////////////////////////////////////////// ■ 조건 변수 개념을 이용해서 재활용할 수 있는 barrier 클래스 템플릿 ■ ZtCBarrier<> 보다 다소 무겁다. ■ event 2 개를 사용한다. 하나는 쓰레드 진입대기용이고 또 하나는 진입 대기한 쓰레드가 모드 풀려나기를 확인하는데 사용한다. ■ 테스트 결과 이상하게 어느 시점에 lock 이 걸리고 있다. 안정화 될때 까지 사용하지 말자. -- 2009-01-22 23:06:00 ///////////////////////////////////////////////////////////////////*/ template< typename TSyncExec=ZNsMain::ZCCriticSectEasy, typename TData =ZNsMain::ZCEmpty > class ZtCCondBarrier : public TData ///////////////////// { public : typedef ZNsMain::ZtCAutoKey CAutoKey; public : enum EStep { EStep_None, EStep_Init, EStep_Wait, EStep_Dead, EStep_Fini };/* enum EStep*/ /*public :*/ protected: TSyncExec mo_CSyncExec; ZTypeID mh_EventID ; ZTypeID mh_EWaitID ; // 대기 목적으로 사용하는 이벤트 int mi_BarrCnt ; int mi_WaitCnt ; EStep me_EStep ; public : ZtCCondBarrier( LPCTSTR AP_EventName =NULL, LPCTSTR AP_EventName2=NULL, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes =NULL, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes2=NULL /*/////////*/ ) { me_EStep =EStep_None; mi_BarrCnt=0 ; mi_WaitCnt=0 ; mh_EventID=::CreateEvent( //////////////////// AP_SemaphoreAttributes , TRUE /*bManualReset*/ , FALSE /*bInitialState*/, AP_EventName /*/////////*/ ); ///////////////////////////// mh_EWaitID=::CreateEvent( //////////////////// AP_SemaphoreAttributes2 , TRUE /*bManualReset*/ , FALSE /*bInitialState*/ , AP_EventName2 /*/////////*/ ); ///////////////////////////// }/* ZtCCondBarrier( LPCTSTR AP_EventName =NULL, LPCTSTR AP_EventName2=NULL, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes =NULL, LPSECURITY_ATTRIBUTES AP_SemaphoreAttributes2=NULL ///////////// ) */ ~ZtCCondBarrier() { ::CloseHandle(mh_EventID); ::CloseHandle(mh_EWaitID); }/* ~ZtCCondBarrier()*/ EStep GetStep() const { return me_EStep; }/* EStep GetStep() const*/ bool IsValidID() const { return mh_EventID!=(HANDLE)ERROR_INVALID_HANDLE; }/* bool IsValidID() const*/ int Init(int AI_Count) { CAutoKey VO_CAutoKey(mo_CSyncExec); if(me_EStep!=EStep_None && me_EStep!=EStep_Fini) return ZNsEnum::ZEBarrier_NO; //endif if(AI_Count<1) AI_Count=1; mi_BarrCnt=AI_Count ; mi_WaitCnt=AI_Count ; me_EStep =EStep_Init; return ZNsEnum::ZEBarrier_OK; }/* int Init(int AI_Count)*/ bool Wait() { mo_CSyncExec.Lock(); if(me_EStep==EStep_Init) { me_EStep=EStep_Wait; } if(me_EStep!=EStep_Wait) { mo_CSyncExec.UnLock(); return false; }/* if(me_EStep!=EStep_Wait)*/ bool VB_IsOK=false; if(--mi_BarrCnt>0) { #ifdef _DEBUG cout<<"♤ Wait Barrier : mi_BarrCnt="< Project -> Settings -> c/c++ -> Category -> Code Generation -> Use run-time library -> 멀티쓰레드 라이브러리 중 선택하여 설정 또는, 컴파일 옵션에 /MT, /MD, /MTd, /MDd 중에 하나를 설정한다. /MT …… 정적링크 /MD …… 동적링크 /MTd …… 정적링크(DEBUG 판) /MDd …… 동적링크(DEBUG 판) ■ -- ////////////////////////////////////////////////////////////////////////////*/ #endif //__ZCPPMAIN__PROCESS_WIN_H__