3161 lines
109 KiB
C++
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__
|