Files
RepoMain/ZCppMain/ZCProcess_Win.H

3161 lines
109 KiB
C++

#ifndef __ZCPPMAIN__PROCESS_WIN_H__
#define __ZCPPMAIN__PROCESS_WIN_H__
#include "ZCppMain/ZMainHead.H"
#include <windows.h>
#include <fstream>
#include <process.h>
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<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
fileout<<"Error !! ZCProcessMutexEasy(LPCTSTR AP_MutexName) Fail To Mutex(Name="<<AP_MutexName<<")"<<std::endl;
fileout.close();
exit(1);
}/*
if(this->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<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
fileout<<"Error !! ZCProcessMutexEasy() Fail To Mutex"<<std::endl;
fileout.close();
exit(1);
}/*
if(this->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<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
fileout<<"Error !! ZCProcessMutexEasy(const ZCProcessMutexEasy& rhs) Fail To Mutex)"<<std::endl;
fileout.close();
exit(1);
}/*
if(this->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<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
fileout<<"Error !! ~ZCProcessMutexEasy() Fail To Close Mutex"<<std::endl;
fileout.close();
exit(1);
}/*
if(this->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<typename TAutoKey> class ZtCAutoKeyRev;
/* ZtCAutoKey<> 템플릿은 Lock 을 설정하고 해제한다.
열고 닫을 수 있는 열쇠와 같아 Key 라고 하였다.
이런 식의 기법을 RAII(Resource Acquisition Is Initialization) 라
고 한다는 것을 알았다. -- 2015-03-10 15:08:00
*/
template<typename TCriticSectEasy=ZCCriticSectEasy> class ZtCAutoKey
{
public :
template<typename TAutoKey> 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() "<<this<<", "<<++msi_CallCnt<<", ▶▶▶▶"<<endl;
#endif //_DEBUG_CAUTOKEY_
mr_SyncEasy.Lock();
}/*
ZtCAutoKey(ZCCriticSectEasy& AR_SyncEasy)*/
~ZtCAutoKey()
{
#ifdef _DEBUG_CAUTOKEY_
cout<<"◀◀◀◀ ZtCAutoKey::~ZtCAutoKey() "<<this<<", "<<--msi_CallCnt<<" ◀◀◀◀"<<endl;
#endif //_DEBUG_CAUTOKEY_
mr_SyncEasy.UnLock();
}/*
~ZtCAutoKey()*/
public:
};/*
template<typename TCriticSectEasy=ZCCriticSectEasy> class ZtCAutoKey */
#ifdef _DEBUG_CAUTOKEY_
template<typename TCriticSectEasy>
int ZtCAutoKey<TCriticSectEasy>::msi_CallCnt = 0;
#endif //_DEBUG_CAUTOKEY_
/*////////////////////////////////////////////////////////////////
■ ZtCAutoKeyRev<> 는 ZtCAutoKey<> 을 생성자에서 인수로 받아서
생성자에서 Lock 을 해제하고, 소멸자에서 다시 Lock 을 걸어 준다.
그런데 이렇게 해야 될 상황 자체를 만들지 말자. 억지로 사용하다
보면 자주 Dead Lock 상태에 빠진다.
※ 2008-04-09 21:01:00
////////////////////////////////////////////////////////////////*/
template<typename TAutoKey> 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<typename TAutoKey> 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 를 쓰기 위해서는 <afxmt.h> 가 필요
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<const TTypeBase&>(rhs);
}/*
ZtCThread(TTypeBase& AR_CBaseType):TTypeBase(AR_CBaseType)*/
template<typename TTypeArg>
ZtCThread(const TTypeArg& AR_TTypeArg):TTypeBase(AR_TTypeArg)
{
_DEBUG_REENTRANT_CHECK_
}/*
template<typename TTypeArg>
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=0;
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=0;
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<ZNsIFace::ZCThread_BASE>
{
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<ZNsIFace::ZCThread_BASE>*/
/*///////////////////////////////////////////////////////////////////////////////
■ 신호상태 : 쓰레드의 실행을 허가하는 상태
자동 리셋 이벤트 : 대기 상태가 종료되면 자동으로 비신호 상태가 된다.
수동 리셋 이벤트 : 쓰레드가 비신호 상태로 만들어 줄 때까지 신호상태를 유지한다.
SetEvent 는 이벤트를 신호상태로 만들고,
ResetEvent 는 이벤트를 비신호상태로 만든다.
(즉 다른 쓰레드가 진입할 수 없는 상태로 만든다.)
자동 리셋 이벤트를 만든 쓰레드가 WaitForSingleObject() 를 호출하면 다른 쓰레드
가 SetEvent() 함수로 그 이벤트를 신호 상태로 만들 때까지 혹은 지정한 시간만큼
블럭된다.
이벤트는 다수의 쓰레드가 어떤 조건을 기다리다가 그 조건이 충족되었을 때 일제히
시작하게 하는 기능을 할 수 있는데 리눅스에서는 판독자/기록자 lock 이나 뮤텍스의
조건 변수로 구현할 수 있다. 물론 세마포어로도 할 수 있다. 다수의 쓰레드가 세마
포어에서 대기하고 있다가 어떤 해제 조건이 되면 세마포어 카운트를 쓰레드 갯수만
큼 늘려주면 모든 쓰레드가 깨어날 것이다.
세마포어를 사용하는 경우, (리눅스에서는 테스트해보지 않았지만) Window 에서는 단
순히 세마포어 카운트를 대기하는 쓰레드 갯수만큼 늘려주어도 모든 대기 쓰레드가
한번에 깨어나지 않았다. 세마포어 카운트를 1 증가시키는 코드를 대기 쓰레드 갯수
만큼 실행해 주어야 한다.
※ 2008-05-05 15:38:00
///////////////////////////////////////////////////////////////////////////////*/
template<typename TData=ZNsMain::ZCEmpty> 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<typename TData=ZNsMain::ZCEmpty> class ZtCEvent */
template<typename TData=ZNsMain::ZCEmpty> 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<TSyncExec> 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="<<mi_BarrCnt<<endl;
#endif //_DEBUG
mo_CSyncExec.UnLock();
{
VB_IsOK=(::WaitForSingleObject(mh_EventID, INFINITE)!=WAIT_FAILED); // 1)
}
mo_CSyncExec.Lock();
#ifdef _DEBUG
cout<<"♠ Wake Barrier : mi_WaitCnt="<<mi_WaitCnt<<endl;
#endif //_DEBUG
if(--mi_WaitCnt<=1)
{
::SetEvent(mh_EWaitID);
}/*
if(--mi_WaitCnt<=1)*/
}
else // mi_BarrCnt<=0
{
VB_IsOK=(::SetEvent(mh_EventID)==TRUE);
#ifdef _DEBUG
cout<<"♣ Wait mi_WaitCnt : mi_WaitCnt="<<mi_WaitCnt<<endl;
#endif //_DEBUG
if(mi_WaitCnt>1)
{
// 모든 쓰레드가 코드 1) 을 통과하기를 기다린다.
mo_CSyncExec.UnLock();
{
WaitForSingleObject(mh_EWaitID, INFINITE);
}
mo_CSyncExec.Lock();
::ResetEvent(mh_EWaitID);
}/*
if(mi_WaitCnt>1)*/
::ResetEvent(mh_EventID);
me_EStep=EStep_Dead;
}/*
else // mi_BarrCnt<=0*/
mo_CSyncExec.UnLock(); return VB_IsOK;
}/*
bool Wait()*/
int Fini()
{
CAutoKey VO_CAutoKey(mo_CSyncExec);
if(me_EStep!=EStep_Dead) return ZNsEnum::ZEBarrier_NO;
me_EStep=EStep_Fini; return ZNsEnum::ZEBarrier_OK;
}/*
int Fini()*/
public:
};/*
template< typename TSyncExec=ZNsMain::ZCCriticSectEasy,
typename TData =ZNsMain::ZCEmpty
>
class ZtCCondBarrier //////////////////////////////////*/
/*////////////////////////////////////////////////////////////
■ 윈도우에는 없는 리눅스 등의 Posix 조건변수를 구현한 클래스.
Posix 에서처럼 뮤텍스와 조건변수가 1:n 이 아니고 1:1 이다.
WaitCond() 멤버를 2번 호출했다면, WakeCond() 멤버도 2번 호
출해야 한다.
■ 세마포어를 쓰고 있는데 부하가 심해서 쓸 수 있을지 모르겠다.
-- 2008-03-05 21:50:00
////////////////////////////////////////////////////////////*/
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCMutexCondSema : public TData
{
protected:
ZTypeID mh_Semaphore;
public :
ZtCMutexCondSema()
{
mh_Semaphore = (HANDLE)ERROR_INVALID_HANDLE;
}/*
ZtCMutexCondSema()*/
ZTypeID GetHandle() const
{
return mh_Semaphore;
}/*
ZTypeID GetHandle() const*/
int InitCond()
{
mh_Semaphore = ::CreateSemaphore(
NULL/*AP_SemaphoreAttributes*/,
0 /*AL_InitialCnt */,
1 /*AL_MaxCnt */,
NULL/*lpSemaphoreName */ );
return mh_Semaphore!=(HANDLE)ERROR_INVALID_HANDLE ? ZNsEnum::ZEThread_OK : ZNsEnum::ZEThread_Invalid ;
}/*
int InitCond()*/
int FiniCond()
{
if(mh_Semaphore==(HANDLE)ERROR_INVALID_HANDLE)
return ZNsEnum::ZEThread_Invalid;
bool VB_IsOK = (::CloseHandle(mh_Semaphore)==TRUE);
mh_Semaphore = (HANDLE)ERROR_INVALID_HANDLE;
return VB_IsOK ? ZNsEnum::ZEThread_OK : ZNsEnum::ZEThread_Invalid ;
}/*
int FiniCond()*/
int WaitCond()
{
return ::WaitForSingleObject(mh_Semaphore, INFINITE);
}/*
int WaitCond()*/
int WaitCondTime(DWORD AI_TimeOutMili)
{
return ::WaitForSingleObject(mh_Semaphore, AI_TimeOutMili); // return ZEThread_TimeOut if timeout
}/*
int WaitCondTime(DWORD AI_TimeOutMili)*/
int WakeCond()
{
if(::ReleaseSemaphore(mh_Semaphore, 1, NULL)!=0) return ZNsEnum::ZEThread_OK;
return ::GetLastError(); /*##############################################*/
}/*
int WakeCond()*/
public:
};/*
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCMutexCondSema : public TData */
/*//////////////////////////////////////////////////////////////////////
■ 리눅스 등의 Posix 조건변수를 Event 로 구현한 클래스. 세마포어로 구현
한 ZtCMutexCondSema<> 보다는 가벼울 것이다. Posix 에서처럼 하나의 조
건변수에 다수의 동기화 object 가 대기할 수 있다.
//////////////////////////////////////////////////////////////////////*/
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCMutexCondEvent : public TData
{
protected:
ZTypeID mh_Event;
public :
ZTypeID GetHandle() const
{
return mh_Event;
}/*
ZTypeID GetHandle() const*/
int InitCond()
{
/*//////////////////////////////////////////////////////
■ HANDLE WINAPI CreateEvent(
__in_opt LPSECURITY_ATTRIBUTES lpEventAttributes,
__in BOOL bManualReset,
__in BOOL bInitialState,
__in_opt LPCTSTR lpName
);
//////////////////////////////////////////////////////*/
return ((mh_Event=::CreateEvent(NULL, TRUE, TRUE, NULL))!=NULL)
? ZNsEnum::ZEThread_OK : ZNsEnum::ZEThread_Invalid ;
//////
}/*
int InitCond()*/
int FiniCond()
{
return ::CloseHandle(mh_Event)==TRUE ? ZNsEnum::ZEThread_OK : ZNsEnum::ZEThread_Invalid ;
}/*
int FiniCond()*/
int WaitCond()
{
// ::ResetEvent() 는 이벤트를 비신호 상태로 만든다.
if(::ResetEvent(mh_Event)==FALSE) return ZNsEnum::ZEThread_Invalid;
return ::WaitForSingleObject(mh_Event, INFINITE); /*###########*/
}/*
int WaitCond()*/
int WakeCond()
{
if(::SetEvent(mh_Event)==TRUE)
return ZNsEnum::ZEThread_OK;
else
return ::GetLastError();
//else
}/*
int WakeCond()*/
public:
};/*
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCMutexCondEvent : public TData */
#if(_CODE_OLD_)
/*//////////////////////////////////////////////////////////////////
■ ZtCMutexCondData<> 는 mutex 와 조건변수를 결합한 클래스다.
■ 멤버 WaitCond() 와 WakeCond() 는 가급적 동기화 영역에서 수행하자.
■ 이전에 조건변수를 TMutexCond 로 구현했던 코드.
이 코드를 SignalObjectAndWait() 를 사용한 버전으로 바꾸었다. 단
이 함수는
supported client : Windows 2000 Professional
supported server : Windows 2000 Server
이상에서 지원한다. -- 2009-04-27 00:20:00
//////////////////////////////////////////////////////////////////*/
template< typename TMutexCond=ZNsMain::ZtCMutexCondEvent<>
>
class ZtCMutexCondData : protected TMutexCond, public ZCProcessMutex
{
public:
typedef TMutexCond CMutexCond;
public:
ZtCMutexCondData()
{
this->ZCProcessMutex::Make();
}/*
ZtCMutexCondData()*/
int Lock()
{
return this->ZCProcessMutex::LockRaw(); // 성공시에 ZEThread_OK 를 반환
}/*
int Lock()*/
int UnLock()
{
return this->ZCProcessMutex::UnLock()==true ? ZNsMain::ZNsEnum::ZEThread_OK : ZNsMain::ZNsEnum::ZEThread_Invalid ;
}/*
int UnLock()*/
int WaitCond()
{
this->ZCProcessMutex::UnLock();
{
this->CMutexCond::WaitCond();
}
this->ZCProcessMutex::Lock();
return ZNsMain::ZNsEnum::ZEThread_OK;
}/*
int WaitCond()*/
using CMutexCond::FiniCond;
using CMutexCond::InitCond;
using CMutexCond::WakeCond;
public:
};/*
template< typename TMutexCond=ZNsMain::ZtCMutexCondEvent<> //////////
>
class ZtCMutexCondData : protected TMutexCond, public ZCProcessMutex */
#endif // if(0)
#if defined(__VISUAL_CPP_VER__) && __VISUAL_CPP_VER__>=200800
/*///////////////////////////////////////////////////////////////////////
■ ZtCMutexCondData<> 은 Linux 의 뮤텍스 조건변수를 구현한 클래스 템플릿.
Critical Section 과 조건변수를 이용하고 있다. Critical Section 말고
Slim Reader-Writer Lock 을 사용해도 된다. 그런데 용법이 다소 번잡해서
Critical Section 을 사용하는 것으로 한다.
-- 2010-04-21 06:08:00
Linux 에서는 뮤텍스가 조건 변수와 결합하는데, Windows 에서는 다르다.
-- 2015-09-16 19:59:00
///////////////////////////////////////////////////////////////////////*/
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCMutexCondData : public TData
{
public :
enum{ESpinCount=100};
private:
::CRITICAL_SECTION mo_CritSect;
::CONDITION_VARIABLE mo_CondVar ;
public :
int InitCond()
{
::InitializeConditionVariable(&mo_CondVar);
if(::InitializeCriticalSectionEx(&mo_CritSect, ESpinCount, 0)==TRUE)
return ZNsEnum::ZEThread_OK ;
else return ZNsEnum::ZEThread_Invalid;
}/*
int InitCond()*/
int FiniCond()
{
::DeleteCriticalSection(&mo_CritSect); return ZNsEnum::ZEThread_OK;
}/*
int FiniCond()*/
int WaitCond(DWORD AI_Mili=INFINITE)
{
if(::SleepConditionVariableCS(&mo_CondVar, &mo_CritSect, AI_Mili)==TRUE)
{
return ZNsEnum::ZEThread_OK;
}/*
if(::SleepConditionVariableCS(&mo_CondVar, &mo_CritSect, AI_Mili)==TRUE)*/
return ::GetLastError()==ERROR_TIMEOUT ? ZNsEnum::ZEThread_TimeOut : ZNsEnum::ZEThread_Invalid ;
/*/////////////////////////////////////////////////////////////////////////////////////////////////
■ dwMilliseconds [in]
SleepConditionVariableCS() 의 3 번 인수 dwMilliseconds 가 경과한 뒤에 대기에서 풀려나지 않으면,
강제로 대기에서 풀려나는데, 이때 0 을 반환한다. 자, 그런데 이때 Critical Section 의 lock 은 다
시 걸려 있을까. 그렇다. SleepConditionVariableCS() 은 timeout 이 발생하면 다시 lock 을 걸고 리
턴한다.
The time-out interval, in milliseconds.
If the time-out interval elapses, the function re-acquires the critical section and returns zero.
If dwMilliseconds is zero, the function tests the states of the specified objects and returns immediately.
If dwMilliseconds is INFINITE, the function's time-out interval never elapses. For more information, see Remarks.
If the function fails or the time-out interval elapses, the return value is zero.
To get extended error information, call GetLastError. Possible error codes include ERROR_TIMEOUT,
which indicates that the time-out interval has elapsed before another thread has attempted to wake
the sleeping thread.
-- 2013-11-11 20:58:00
/////////////////////////////////////////////////////////////////////////////////////////////////*/
}/*
int WaitCond(DWORD AI_Mili=INFINITE)*/
int WakeCond (){::WakeConditionVariable (&mo_CondVar ); return ZNsEnum::ZEThread_OK;}
int WakeAllCond(){::WakeAllConditionVariable(&mo_CondVar ); return ZNsEnum::ZEThread_OK;}
int Lock (){::EnterCriticalSection (&mo_CritSect); return ZNsEnum::ZEThread_OK;}
int UnLock (){::LeaveCriticalSection (&mo_CritSect); return ZNsEnum::ZEThread_OK;}
public:
};/*
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCMutexCondData : public TData */
#else // !( defined(__VISUAL_CPP_VER__) && __VISUAL_CPP_VER__>=200800 )
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCMutexCondData : public TData
{
private:
ZTypeID mh_MutexSync; // 동기화용 뮤텍스
ZTypeID mh_EventWait; // 대기용 이벤트
bool mb_IsOnWait ;
public :
ZtCMutexCondData()
{
mh_MutexSync=NULL ;
mh_EventWait=NULL ;
mb_IsOnWait =false;
}/*
ZtCMutexCondData()*/
int Lock(DWORD ADW_TimeOut=INFINITE)
{
DWORD VI_Return = ::WaitForSingleObject(mh_MutexSync, ADW_TimeOut); /*/////////////////////////*/
return (VI_Return!=WAIT_ABANDONED && VI_Return!=WAIT_FAILED) ? ZNsEnum::ZEThread_OK : VI_Return ;
}/*
int Lock(DWORD ADW_TimeOut=INFINITE)*/
int UnLock()
{
return (::ReleaseMutex(mh_MutexSync)==TRUE) ? ZNsEnum::ZEThread_OK : ZNsEnum::ZEThread_Invalid ;
}/*
int UnLock()*/
int InitCond()
{
mh_MutexSync = ::CreateMutex(NULL, FALSE, NULL);
if(mh_MutexSync==NULL) return ::GetLastError();
mh_EventWait = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if(mh_EventWait==NULL)
return ::GetLastError();
else
return ZNsEnum::ZEThread_OK;
//else
}/*
int InitCond()*/
int WaitCond(DWORD ADW_TimeOut=INFINITE)
{
/*//////////////////////////////////////////////////////////////////
■ DWORD WINAPI SignalObjectAndWait(
__in HANDLE hObjectToSignal,
__in HANDLE hObjectToWaitOn,
__in DWORD dwMilliseconds ,
__in BOOL bAlertable
);
HANDLE hObjectToSignal 은 semaphore, mutex, event 가 될 수 있다.
//////////////////////////////////////////////////////////////////*/
if(mb_IsOnWait==false)
{
mb_IsOnWait=true; ::ResetEvent(mh_EventWait); // 이벤트를 비신호 상태로 만든다.
}/*
if(mb_IsOnWait==false)*/
DWORD VI_Return = ::SignalObjectAndWait
( mh_MutexSync, mh_EventWait, ADW_TimeOut, FALSE );
#if(_CODE_BAD_)
return (VI_Return==WAIT_OBJECT_0) ? Lock() : VI_Return;
#else
if(VI_Return==WAIT_OBJECT_0) return Lock();
if(VI_Return==WAIT_TIMEOUT) Lock(); return VI_Return; // -- 2013-11-11 21:10:00
#endif
/*/////////////////////////////////////////////////////////////
■ SignalObjectAndWait() 로 대기하다가, 특정 조건에서 깨어나면,
다시 Lock() 을 수행하여 뮤텍스를 소유하고 있음에 주의. 결국
pthread 보다 조건변수를 사용하기가 약간 복잡하다.
-- 2009-04-28 08:36:00
■ SignalObjectAndWait() 이 WAIT_TIMEOUT 을 반환한 경우에도 lock
을 걸고 리턴하는 것이 옳다. 그래야
Lock() => WaitCond() => UnLock() 과
Lock() => WakeCond() => UnLock()
의 일관적인 호출을 할 수 있다. -- 2013-11-11 21:10:00
/////////////////////////////////////////////////////////////*/
}/*
int WaitCond(DWORD ADW_TimeOut=INFINITE)*/
int WakeCond()
{
if(mb_IsOnWait==true) mb_IsOnWait=false;
return ::SetEvent(mh_EventWait)==TRUE ?
ZNsEnum::ZEThread_OK :
::GetLastError() ;
}/*
int WakeCond()*/
int FiniCond()
{
const bool CB_IsOK1=(::CloseHandle(mh_MutexSync)==TRUE);
const bool CB_IsOK2=(::CloseHandle(mh_EventWait)==TRUE);
return (CB_IsOK1 && CB_IsOK2) ? ZNsEnum::ZEThread_OK : ZNsEnum::ZEThread_Invalid ;
}/*
int FiniCond()*/
public:
};/*
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCMutexCondData //////////////////*/
#endif // !(defined(__VISUAL_CPP_VER__) && __VISUAL_CPP_VER__>=200800)
typedef ZtCMutexCondData<> ZCEventCond; // for compatiblity with Linux
/*////////////////////////////////////////////////////////////////////////////////////
■ class ZtCAtomicIntSync<>
커널이나 라이브러리의 동기화 object 를 사용하지 않고 동기화를 구현하고 싶은 경우에
사용하는 클래스 템플릿. volatile 정수에 대한 원자적 연산을 이용하고 있다.
일종의 lock-free 다.
■ VOID WINAPI Sleep(__in DWORD dwMilliseconds);
dwMilliseconds :
A value of zero causes the thread to relinquish the remainder of its time slice
to any other thread of equal priority that is ready to run.
If there are no other threads of equal
relinquish :
[타동사][VN] ~ sth (to sb) (격식) (특히 마지못해 소유권 등을) 포기하다[내주다]
■ LONG InterlockedCompareExchange(
IN OUT PLONG Destination, IN LONG Exchange, IN LONG Comparand);
If Comparand is equal to *Destination, then *Destination is set to equal Exchange.
Otherwise, *Destination is unchanged.
■ ZtCAtomicIntSync<> 를 구현하면서, 정수의 atomic 연산을 이용할 수 없는 경우,
ZtCAtomicIntSync<>::EUseAtomicInt 을 0 으로 설정한다. 정수의 atomic 연산을 이용할
수 있다면 1 이다.
■ -- 2010-04-03 18:05:00
■ 리눅스 등에서 ZtCAtomicIntSync<> 이 동기화 object 를 사용해서 (무겁게) 구현되는 경
우, ZtCAtomicIntSync<>::TypeSync 타입을 통해서 어떤 동기화 object 를 사용했는지를
나타낼 것이다.
-- 2010-04-17 21:29:00
////////////////////////////////////////////////////////////////////////////////////*/
template< typename TTypeBase=ZNsMain::ZCEmpty
>
class ZtCAtomicIntSync : public TTypeBase
{
public :
typedef ZtCAtomicIntSync TypeSync;
public :
enum{EUseAtomicInt=1};
public :
enum ESync
{
ESync_Lock =0,
ESync_UnLock=1
};/*
enum ESync*/
private:
volatile LONG ml_SyncState;
public :
ZtCAtomicIntSync()
{
ml_SyncState=ESync_UnLock;
}/*
ZtCAtomicIntSync()*/
ZtCAtomicIntSync(const TTypeBase& rhs) : TTypeBase(rhs)
{
ml_SyncState=ESync_UnLock;
}/*
ZtCAtomicIntSync(const TTypeBase& rhs)*/
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs) : TTypeBase(rhs)
{
ml_SyncState=ESync_UnLock;
}/*
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs)*/
ZtCAtomicIntSync& operator=(const TTypeBase& rhs)
{
this->TTypeBase::operator=(rhs); return *this;
}/*
ZtCAtomicIntSync& operator=(const TTypeBase& rhs)*/
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)
{
this->TTypeBase::operator=(rhs); return *this;
}/*
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)*/
void Lock()
{
/* &ml_SyncState 과 ESync_UnLock 과 같을 때만,
ml_SyncState 을 ESync_Lock 으로 설정. */
#define __INTERLOCKED_COMP_EXCHANGE__ \
::InterlockedCompareExchange(&ml_SyncState, ESync_Lock, ESync_UnLock)
while(__INTERLOCKED_COMP_EXCHANGE__==ESync_Lock)
{
// 아직 Lock 이 걸려 있는 경우이다.
::Sleep(0);
}/*
while(__INTERLOCKED_COMP_EXCHANGE__==ESync_Lock)*/
#undef __INTERLOCKED_COMP_EXCHANGE__
}/*
void Lock()*/
void UnLock()
{
ml_SyncState=ESync_UnLock;
}/*
void UnLock()*/
public:
};/*
template< typename TTypeBase=ZNsMain::ZCEmpty
>
class ZtCAtomicIntSync : public TTypeBase */
template<> class ZtCAtomicIntSync<ZNsMain::ZCEmpty>
{
public :
enum{EUseAtomicInt=1};
public :
enum ESync
{
ESync_Lock =0,
ESync_UnLock=1
};/*
enum ESync*/
private:
volatile LONG ml_SyncState;
public :
ZtCAtomicIntSync()
{
ml_SyncState=ESync_UnLock;
}/*
ZtCAtomicIntSync()*/
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs)
{
ml_SyncState=ESync_UnLock;
}/*
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs)*/
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)
{
return *this;
}/*
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)*/
void Lock()
{
#define __INTERLOCKED_COMP_EXCHANGE__ \
::InterlockedCompareExchange(&ml_SyncState, ESync_Lock, ESync_UnLock)
while(__INTERLOCKED_COMP_EXCHANGE__==ESync_Lock)
{
// 아직 Lock 이 걸려 있는 경우이다.
::Sleep(0);
}/*
while(__INTERLOCKED_COMP_EXCHANGE__==ESync_Lock)*/
#undef __INTERLOCKED_COMP_EXCHANGE__
}/*
void Lock()*/
void UnLock()
{
ml_SyncState=ESync_UnLock;
}/*
void UnLock()*/
public:
};/*
template<> class ZtCAtomicIntSync<ZNsMain::ZCEmpty>*/
#if defined(__VISUAL_CPP_VER__) && __VISUAL_CPP_VER__>=200800
/*//////////////////////////////////////////////////////////////////////////////
■ Slim Reader-Writer Lock. 리눅스의 'read-write lock lock' 다.
■ 기존 CriticalSection 비해 조금 더 적은 메모리 사용과 빠른 수행 속도를 가진다.
Reader 쓰레드와 Writer 쓰레드가 완전히 분리될 수 있을 경우 사용될 수 있다.
■ Recursively lock을 사용할 수가 없고 Owning thread를 알 수 없다.
-- 2013-07-13 20:56:00
//////////////////////////////////////////////////////////////////////////////*/
class ZCSRWLock
{
private:
::RTL_SRWLOCK mo_SRWLOCK;
public :
void Init (){::InitializeSRWLock (&mo_SRWLOCK);}
void LockRead (){::AcquireSRWLockShared (&mo_SRWLOCK);}
void UnLockRead (){::ReleaseSRWLockShared (&mo_SRWLOCK);}
void LockWrite (){::AcquireSRWLockExclusive(&mo_SRWLOCK);}
void UnLockWrite(){::ReleaseSRWLockExclusive(&mo_SRWLOCK);}
bool WaitCond(::PCONDITION_VARIABLE AP_Cond, ULONG AL_Flag=0, DWORD AI_MiliSec=INFINITE)
{
// AL_Flag 이 CONDITION_VARIABLE_LOCKMODE_SHARED 이면 Read Lock, 그렇지 않으면 Write Lock 이다.
return ::SleepConditionVariableSRW(AP_Cond, &mo_SRWLOCK, AI_MiliSec, AL_Flag)==TRUE;
}/*
bool WaitCond(::PCONDITION_VARIABLE AP_Cond, ULONG AL_Flag=0, DWORD AI_MiliSec=INFINITE)*/
public:
};/*
class ZCSRWLock*/
class ZCLockRW // for linux compatiblity
{
public :
enum ELockType
{
ELockType_None,
ELockType_Read,
ELockType_Write
};/*
enum ELockType*/
private:
::RTL_SRWLOCK mo_SRWLOCK ;
ELockType me_ELockType;
public :
ZCLockRW()
{
me_ELockType=ELockType_None;
}/*
ZCLockRW()*/
void Init()
{
::InitializeSRWLock(&mo_SRWLOCK);
}/*
void Init()*/
void LockRead(){
::AcquireSRWLockShared (&mo_SRWLOCK); me_ELockType=ELockType_Read ;}
void LockWrite(){
::AcquireSRWLockExclusive(&mo_SRWLOCK); me_ELockType=ELockType_Write;}
void UnLock()
{
/* me_ELockType 을 설정하는 연산은 동기화 영역에서 수행되고 있음에 주의한다. */
if(me_ELockType==ELockType_Read)
{
me_ELockType=ELockType_None; ::ReleaseSRWLockShared (&mo_SRWLOCK);
}
if(me_ELockType==ELockType_Write)
{
me_ELockType=ELockType_None; ::ReleaseSRWLockExclusive(&mo_SRWLOCK);
}/*
if(me_ELockType==ELockType_Write)*/
}/*
void UnLock()*/
bool WaitCond(::PCONDITION_VARIABLE AP_Cond, ULONG AL_Flag=0, DWORD AI_MiliSec=INFINITE)
{
// linux 의 ZCLockRW 에는 없는 멤버 함수
return ::SleepConditionVariableSRW(AP_Cond, &mo_SRWLOCK, AI_MiliSec,AL_Flag)==TRUE;
}/*
bool WaitCond(::PCONDITION_VARIABLE AP_Cond, ULONG AL_Flag=0, DWORD AI_MiliSec=INFINITE)*/
public:
};/*
class ZCLockRW*/
#endif //defined(__VISUAL_CPP_VER__) && __VISUAL_CPP_VER__>=200800
/*////////////////////////////////////////////////////////////////////////////////////////////////////
■ named pipe 를 사용할 때, 일반적으로 서버측에서는 무한루프에서 CreateNamedPipe() 로 파이프를 만들고
ConnectNamedPipe() 로 클라이언트의 연결요청을 기다린다. 이 후에 클라이언트에서는 WaitNamedPipe() 로
'이미 생성된 pipe' 로 접속을 시도하여 성공하면 CreateFile() 로 pipe 핸들을 얻는다. 이때 다른 쓰레드
가 CreateFile() 로 얻을 수 있는 pipe 핸들을 먼저 가져갈 수 있으므로, 반환값을 체크하여 유효한 핸들
인지 조사한다.
서버쪽에서 CreateNamedPipe() 로 파이프를 쓰기용으로 열었다면, 클라이언트 쪽에서는 CreateFile() 함수
로 파이프를 읽기용으로 열어야 한다. 즉 서로 반대의 입출력 모드로 열어야 한다.
PIPE_ACCESS_OUTBOUND ↔ GENERIC_READ
PIPE_ACCESS_INBOUND ↔ GENERIC_WRITE
이후에 서버측에서는 ConnectNamedPipe() 가 리턴하고 이때부터 해당 pipe 에 적절한 처리를 해주면 된다.
WaitNamedPipe() 로 해당 이름의 pipe 로의 접속을 시도했는데, 그 pipe 가 아직 서버측에서 만들어지지
않았다면 대기인수에 상관없이 바로 리턴해버린다. 따라서 이에 대한 처리를 해주어야 한다.
■ pipe 통신이 종료되면 서버는 FlushFileBuffer() 함수를 호출하여 클라이언트가 미처 읽지 못한 데이타를
다 읽을 때까지 기다려 주어야 한다. 그리고 DisconnectNamedPipe() 함수로 접속을 종료하고 CloseHandle()
함수로 파이프를 파괴한다. 김상형 저 Window API 정복 1134 page.
////////////////////////////////////////////////////////////////////////////////////////////////////*/
class ZCPipe
{
protected:
HANDLE mh_Pipe ;
DWORD mi_ByteOfSended;
DWORD mi_ByteOfRecved;
public :
enum
{
ZEPIPE_OutBuffSize = 1024*8,
ZEPIPE_InBuffSize = 1024*8
};/*
enum*/
public :
ZCPipe()
{
InitVar();
}/*
ZCPipe()*/
void InitVar()
{
mh_Pipe =0;
mi_ByteOfSended=0;
mi_ByteOfRecved=0;
}/*
void InitVar()*/
void InitByteVar()
{
mi_ByteOfSended=0;
mi_ByteOfRecved=0;
}/*
void InitByteVar()*/
DWORD GetByteOfSended() const
{
return mi_ByteOfSended;
}/*
DWORD GetByteOfSended() const*/
DWORD GetByteOfRecved() const
{
return mi_ByteOfRecved;
}/*
DWORD GetByteOfRecved() const*/
// 이름 없는 파이프를 생성한다.
static bool Create(PHANDLE APH_ReadPipe, PHANDLE APH_WritePipe, LPSECURITY_ATTRIBUTES AP_PipeAtt, DWORD AI_Size=0)
{
return ::CreatePipe(APH_ReadPipe, APH_WritePipe, AP_PipeAtt, AI_Size)==TRUE;
}/*
static bool Create(PHANDLE APH_ReadPipe, PHANDLE APH_WritePipe, LPSECURITY_ATTRIBUTES AP_PipeAtt, DWORD AI_Size=0)*/
HANDLE CreateNamed /*######################################################*/
(
LPCTSTR AP_Name
, DWORD AI_OpenMode = PIPE_ACCESS_DUPLEX
, DWORD AI_PipeMode = PIPE_TYPE_BYTE
, DWORD AI_MaxInstance = PIPE_UNLIMITED_INSTANCES
, DWORD AI_OutBufferSize = ZEPIPE_OutBuffSize
, DWORD AI_InBufferSize = ZEPIPE_InBuffSize
, DWORD AI_DefaultTimeOut = 0
, LPSECURITY_ATTRIBUTES AP_SecAtt = NULL
)
/*#########################################################################*/
{
/*///////////////////////////////////////////////////////////////////////
■ 파이프명 AP_Name 은
\\서버명\pipe\파이프명
의 형식이다. local 의 서버명은 "." 이다.
파이프명이 MyPipe 라면 파이프표현 문자열은 "\\\\.\\pipe\\MyPipe" 이다.
///////////////////////////////////////////////////////////////////////*/
return mh_Pipe = ::CreateNamedPipe(
AP_Name, AI_OpenMode, AI_PipeMode, AI_MaxInstance, AI_OutBufferSize, AI_InBufferSize, AI_DefaultTimeOut, AP_SecAtt) ;
}/*
HANDLE CreateNamed /*########################################################
(
LPCTSTR AP_Name
, DWORD AI_OpenMode = PIPE_ACCESS_DUPLEX
, DWORD AI_PipeMode = PIPE_TYPE_BYTE
, DWORD AI_MaxInstance = PIPE_UNLIMITED_INSTANCES
, DWORD AI_OutBufferSize = ZEPIPE_OutBuffSize
, DWORD AI_InBufferSize = ZEPIPE_InBuffSize
, DWORD AI_DefaultTimeOut = 0
, LPSECURITY_ATTRIBUTES AP_SecAtt = NULL
)
###########################################################################*/
bool WaitConnect(LPOVERLAPPED AP_LPOVERLAPPED=0)
{
return ::ConnectNamedPipe(mh_Pipe, AP_LPOVERLAPPED)==TRUE || (::GetLastError()==ERROR_PIPE_CONNECTED);
/*///////////////////////////////////////////////////////////////////////////////////////////////
■ ::GetLastError()==ERROR_PIPE_CONNECTED 인 경우도 클라이언트로부터 정상적으로 연결이 된 것이다.
서버측에서 CreateNamedPipe() 를 호출한 후, ConnectNamedPipe() 를 호출하기 전에, 클라이언트측에
서 접속에 성공하면, 서버측의 ConnectNamedPipe() 가 FALSE 를 반환하면서
::GetLastError()==ERROR_PIPE_CONNECTED
가 되는 것이다.
///////////////////////////////////////////////////////////////////////////////////////////////*/
}/*
bool WaitConnect(LPOVERLAPPED AP_LPOVERLAPPED=0)*/
/*///////////////////////////////////////////////////////////////////////
BOOL ::WaitNamedPipe(AP_PipeName,AI_TimeOut);
이 함수는 첫번째 인수가 지정하는 파이프의 인스턴스가 사용가능해질 때까지 두
번째 인수가 지정하는 시간동안 대기한다. 만약 인스턴스가 하나도 생성되어 있
지 않으면 대기 시간에 상관없이 리턴해버린다. 그래서 이 함수는 아직 생성되지
도 않은 파이프가 생성되기를 기다리는 것이 아니라 이미 생성된 파이프의 인스
턴스 중 하나가 사용가능해지기를 기다린다. 이 함수가 TRUE 를 리턴하면 즉시
CreateFile 함수로 파이프를 열어야 한다. 그러나 그 사이에 다른 쓰레드가 끼어
들 수 있으므로 파이프가 제대로 열렸는지 점검해 보고 그렇지 않으면 얌전히 대
기상태로 들어간다.
///////////////////////////////////////////////////////////////////////*/
static bool WaitPipe(LPCTSTR AP_PipeName, DWORD AI_TimeOut=NMPWAIT_WAIT_FOREVER)
{
return ::WaitNamedPipe(AP_PipeName, AI_TimeOut)==TRUE;
}/*
static bool WaitPipe(LPCTSTR AP_PipeName, DWORD AI_TimeOut=NMPWAIT_WAIT_FOREVER)*/
HANDLE CreateFile /*////////////////////////////////////////////////*/
(
LPCTSTR AP_PipeName
, DWORD AI_DesiredAccess = FILE_SHARE_READ | FILE_SHARE_WRITE
, DWORD AI_SharedMode = 0
, LPSECURITY_ATTRIBUTES AP_SecuAtt= NULL
, DWORD AI_CreationDisposition = OPEN_EXISTING
, DWORD AI_FlagAtt = 0
, HANDLE AH_Template = NULL
)
/*//////////////////////////////////////////////////////////////////*/
{
return mh_Pipe = ::CreateFile(
AP_PipeName, AI_DesiredAccess, AI_SharedMode, AP_SecuAtt, AI_CreationDisposition, AI_FlagAtt, AH_Template);
}/*
HANDLE CreateFile
(
LPCTSTR AP_PipeName
, DWORD AI_DesiredAccess = FILE_SHARE_READ | FILE_SHARE_WRITE
, DWORD AI_SharedMode = 0
, LPSECURITY_ATTRIBUTES AP_SecuAtt= NULL
, DWORD AI_CreationDisposition = OPEN_EXISTING
, DWORD AI_FlagAtt = 0
, HANDLE AH_Template = NULL
)
/*//////////////////////////////////////////////////////////////////*/
bool Write(LPCVOID AP_Buffer, DWORD AI_BytesToWrite, LPOVERLAPPED AP_LPOVERLAPPED=0)
{
return ::WriteFile(mh_Pipe, AP_Buffer, AI_BytesToWrite, &mi_ByteOfSended, AP_LPOVERLAPPED)==TRUE;
}/*
bool Write(LPCVOID AP_Buffer, DWORD AI_BytesToWrite, LPOVERLAPPED AP_LPOVERLAPPED=0)*/
bool Write(HANDLE AH_Pipe, LPCVOID AP_Buffer, DWORD AI_BytesToWrite, LPOVERLAPPED AP_LPOVERLAPPED=0)
{
return ::WriteFile(AH_Pipe, AP_Buffer, AI_BytesToWrite, &mi_ByteOfSended, AP_LPOVERLAPPED)==TRUE;
}/*
bool Write(HANDLE AH_Pipe, LPCVOID AP_Buffer, DWORD AI_BytesToWrite, LPOVERLAPPED AP_LPOVERLAPPED=0)*/
bool Read(LPVOID AP_Buffer, DWORD AI_BytesToRead, LPOVERLAPPED AP_LPOVERLAPPED=0)
{
return ::ReadFile(mh_Pipe, AP_Buffer, AI_BytesToRead, &mi_ByteOfRecved, AP_LPOVERLAPPED)==TRUE;
}/*
bool Read(LPVOID AP_Buffer, DWORD AI_BytesToRead, LPOVERLAPPED AP_LPOVERLAPPED=0)*/
bool Read(HANDLE AH_Pipe, LPVOID AP_Buffer, DWORD AI_BytesToRead, LPOVERLAPPED AP_LPOVERLAPPED=0)
{
return ::ReadFile(AH_Pipe, AP_Buffer, AI_BytesToRead, &mi_ByteOfRecved, AP_LPOVERLAPPED)==TRUE;
}/*
bool Read(HANDLE AH_Pipe, LPVOID AP_Buffer, DWORD AI_BytesToRead, LPOVERLAPPED AP_LPOVERLAPPED=0)*/
/*/////////////////////////////////////////////////
BOOL FlushFileBuffers(HANDLE hFile);
hFile
[in] A handle to an open file.
The file handle must have the GENERIC_WRITE access right.
For more information, see File Security and Access Rights.
If hFile is a handle to a communications device,
the function only flushes the transmit buffer.
If hFile is a handle to the server end of a named pipe,
the function does not return
until the client has read all buffered data from the pipe.
-- hFile 이 서버측 named pipe 핸들이면
-- client 가 pipe 버퍼에 남아 있는 데이타를
-- 모두 수신할 때까지 리턴하지 않고 대기한다.
////////////////////////////////////////////////*/
bool Flush()
{
return ::FlushFileBuffers(mh_Pipe)==TRUE;
}/*
bool Flush()*/
void CloseConnect()
{
::DisconnectNamedPipe(mh_Pipe);
}/*
void CloseConnect()*/
void CloseHandle()
{
::CloseHandle(mh_Pipe);
}/*
void CloseHandle()*/
void Close()
{
::DisconnectNamedPipe(mh_Pipe);
::CloseHandle(mh_Pipe);
}/*
void Close()*/
public:
};/*
class ZCPipe*/
}/*
namespace ZNsMain*/
/*////////////////////////////////////////////////////////////////////////////
■ 멀티쓰레드 함수를 사용하는 코드에서 링크 에러가 발생할 경우
원인 :
_beginthread(), _beginthreadex(), _endthread(), _endthreadex()
등의 멀티쓰레드 함수를 사용할때는 멀티쓰레드용 라이브러리를
링크할 필요가 있다.
대처법 :
VC++ -> 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__