DirectPlay DirectPlay 의 고도의 주제 DirectPlay 콜백 함수와 multi-thread에 관한 문제   [목차열람] [주소복사] [슬롯비우기]
DirectPlay 콜백 함수와 multi-thread에 관한 문제
 
Microsoft DirectX 9.0

DirectPlay 콜백 함수와 multi-thread에 관한 문제


Microsoft® DirectPlay® 와 DirectPlay Voice 에서는, DirectPlay 가 발행하는 이벤트를 처리하기 위해(때문에), 몇개의 콜백 함수를 독자적으로 처리 할 필요가 있다. DirectPlay 는 multi-thread화 되고 있으므로, 동시에 복수의 이벤트를 발행하는 일이 있다. 이 때문에, 중복 하는 복수의 콜백이 애플리케이션에 닿을 가능성이 있다.

DirectPlay 에는, 콜백의 지시에 대응하기 위해서, thread 풀이 보관 유지되고 있다. 콜백은, DirectPlay 가 보관 유지하는 풀내의 thread로부터 호출된다. Microsoft Windows® 2000 및 Windows XP 에서는, 이 thread 풀의 사이즈를 프로세스 마다 설정할 수 있다. 또, DirectPlay 는, Windows 2000 및 Windows XP 상에서 실행될 때 I/O 완료 포트를 사용한다. I/O 완료 포트는 고도의 내용이며, 여기에서는 설명하지 않는다. 더 자세한 정보는, MSDN (Microsoft Developer Network )의 문서나, Microsoft Win32® 의 multi-thread에 관한 현재 입수 가능한 레퍼런스를 참조할것을 추천 한다.

따라서, DirectPlay 콜백으로 올바르게 신뢰성의 높은 데이터 액세스를 실시하려면 , 동기를 multi-thread화하는 수단을 처리 해야 한다. 이것을, 콜백을재입가능또는thread 세이프로 한다고 부른다.

Windows 패밀리에서는, 현재, multi-thread 환경에서 데이터를 동기 하기 위해서, 다음의 3 개의 방법이 준비되어 있다.

크리티컬 섹션 개체를 사용한 동기 방법은, Microsoft DirectX® 9.0 SDK 에 부속되는 DirectPlay Voice 샘플에 나타나고 있다. 뮤텍스 개체 또는 semaphore 개체를 처리 하는 경우, Microsoft Platform Software Development Kit (SDK)나 많은 참고서적에 설명이 기재되어 있다. 이러한 동기 방법을 처리 하는 경우, 무엇인가 문제가 일어났을 때에 복잡하고 어려운 디버그 작업이 필요하게 되므로, 각각의 분야에 관한 상세한 지식이 필요하다.

DirectPlay 의 thread 모델은, 최고의 효율을 얻을 수 있도록(듯이) 최적화되고 있다. 이 때문에, 수신 메시지를 포함한 "지시" 메시지중에, thread 문맥이 바뀔 것은 없다.

더 자세한 정보는,「DirectPlay 와 DirectPlay Voice 에 있어서의 콜백 함수의 처리」을 참조할것.

DirectPlay 의 네트워킹 콜백

DirectPlay 의 네트워킹 콜백 함수는,PFNDPNMESSAGEHANDLER 형이다. 네트워킹 세션의 종류에 응해,IDirectPlay8Peer::Initialize ,IDirectPlay8Client::Initialize , 또는 IDirectPlay8Server::Initialize 를 사용해 콜백 함수의 주소를 등록한다.

동기에 관한 문제

DirectPlay 콜백에서의 처리중에 게임 데이터의 정합성을 유지하기 위해서(때문에)는, 3 개의 thread 동기 개체의 어느쪽이든을 채용할 필요가 있다.

게임 데이터가 어떻게 파손하는지를 설명하기 위해(때문에), 콜백으로 구조체에 게임 데이터의 패킷을 삽입하는 경우에 대해 생각한다. 콜백은 재입 가능해서, 최초의 콜백이 완료하기 전에, 다른 thread가 콜백을 실행할 수 있다. 이 2 번째의 thread가, 메모리의 같은 장소에 있는 구조체에 액세스 해, 데이터를 변경하려고 하는 일이 있다. 이 결과, 1 번째의 thread가 구조체에 둔 데이터가, 2 번째의 thread가 구조체에 둔 데이터로 덧쓰기된다. 이것은 multi-thread의 매우 간단한 예이며, thread가 정상적으로 동기 하지 않게 되는 원인은 그 밖에도 여러 가지 있으므로, 주의가 필요하다.

DirectPlay 네트워킹 세션으로 데이터를 동기 하는 방법의 예에 대해서는,「크리티컬 섹션 개체를 사용한 DirectPlay 네트워킹 콜백의 처리」을 참조할것.

워커 thread

"워커 thread" 를 자작하는 것도 1 개의 방법이다. 워커 thread는, 애플리케이션으로 정의되는 이제 1 개의 multi-thread 콜백이며, DirectPlay 콜백과 관계없이 게임 데이터를 처리하기 위해서 생성 된다. 이 목적을 달성하기 위해서, 가장 일반적인 수단으로서 DirectPlay 의 네트워킹 콜백 thread에서는, 받은 데이터를 버퍼에 보존한다. 다음에, 새로운 thread가 생성 되어 버퍼링 데이터를 처리하도록(듯이) 알리는 메시지가 워커 thread 콜백에 송신된다.

multi-thread의 퍼포먼스에 관한 문제와 비동기 처리

DirectPlay 콜백내에서 메시지의 처리에 걸치는 시간은, 신중하게 검토할 필요가 있다. DirectPlay 콜백내에서 대량의 데이터를 처리해, 데이터를 잠그는 메카니즘을 사용해 thread의 동기를 잡으면 다른 thread가 콜백을 실행하기 위해서 대기할 경우에 블록 문제가 일어난다.

한편, 워커 thread를 처리 해, 게임 데이터의 처리를 다른 콜백에 맡기면 자작의 thread와 DirectPlay 로 생성 된 thread의 사이에 CPU 가 문맥을 바꾸기 (위해)때문에, 오버헤드가 큰폭으로 증가하는 위험성이 있다. 이 방법을 채용할 수 있는 것은, 게임 데이터의 처리에 매우 시간이 걸려, 게임의 리얼 타임 처리에 있어 빠뜨릴 수 없는 데이터가 아닌 경우에 한정된다. 예를 들어, player의 위치 데이터는, 게임내에서 player를 리얼타임에 배치하기 위해서 빠뜨릴 수 없는 데이터이므로, 워커 thread로 처리하는 것은 추천하지 않는다.

콜백으로부터 DPNSUCCESS_PENDING 를 돌려주어, 데이터 버퍼의 포인터를 생성 해, 워커 thread가 그 포인터를 이용할 수 있도록(듯이) 하는 일도 가능하다. 워커 thread는, 게임 데이터의 처리를 종료 하면, 사용하고 있는 위상기하학에 응해,IDirectPlay8Peer ,IDirectPlay8Client , 또는 IDirectPlay8Server 의 ReturnBuffer 메서드를 호출한다.

API 호출에 걸치는 잠금의 보관 유지

보통, API 호출에 걸쳐 공유 리소스의 잠금을 보관 유지하는 것은 피할 필요가 있다. 이것은, 다른 thread와의 교환을 모두 상정하는 것이 곤란하기 때문에이다. 다음 코드에서는, 동기적으로 IDirectPlay8Peer::SendTo 를 호출하고 있을 때, 송신 thread가 잘못해 pObj->csSomeLock 크리티컬 섹션을 보관 유지하고 있다.

typedef struct _MYOBJECT{
	CRITICAL_SECTION    csSomeLock;
	DWORD               dwFlags;
	.
	.
	.
} MYOBJECT, *PMYOBJECT;

IDirectPlay8Peer    *pDP8Peer;
PMYOBJECT           pObj;
.
.
.
EnterCriticalSection(&pObj->csSomeLock);
pDP8Peer->SendTo(DPNID_ALL_PLAYERS_GROUP,
                 &dpnBuffer, 1, 0,
                 NULL, NULL, DPNSEND_SYNC);
LeaveCriticalSection(&pObj->csSomeLock);

DPNSEND_NOLOOPBACK 플래그가 사용되지 않기 때문에, 로컬 player는, 다른 thread에 있는 애플리케이션의 메시지 핸들러의 호출에 의해, 메시지의 복사를 수신한다. 이 메시지 핸들러가 메시지에 응답해 pObj->csSomeLock 를 취득하려고 하면 데드 록 상태가 발생한다. 이것은, 메시지 핸들러가 값을 돌려줄 때까지 송신 thread는 IDirectPlay8Peer::SendTo 로부터 돌아올 수 없는 (따라 잠금을 삭제할 수 없다)가, 메시지 핸들러는 송신 thread가 잠금을 삭제할 때까지 값을 돌려줄 수 없기 때문이다. 이 대신에, 플래그 또는 인덱스 시스템을 사용해, API 호출을 실행중에 잠금을 릴리즈 할 수 있도록(듯이) 한다.

typedef struct _MYOBJECT{
	CRITICAL_SECTION    csSomeLock;
	DWORD               dwFlags;
	.
	.
	.
} MYOBJECT, *PMYOBJECT;

IDirectPlay8Peer     *pDP8Peer;
PMYOBJECT            pObj;
.
.
.
EnterCriticalSection(&pObj->csSomeLock);
pObj->dwFlags |= FLAGS_SENDING;
LeaveCriticalSection(&pObj->csSomeLock);

pDP8Peer->SendTo(DPNID_ALL_PLAYERS_GROUP,
                 &dpnBuffer, 1, 0,
                 NULL, NULL, DPNSEND_SYNC);
				 
EnterCriticalSection(&pObj->csSomeLock);
pObj->dwFlags &= ~FLAGS_SENDING;
LeaveCriticalSection(&pObj->csSomeLock);


© 2002 Microsoft Corporation. All rights reserved.
↑TOP