CodeMaster 님이 쓰신 글 :
: 님이 쓰신 글 :
: : CodeMaster 님이 쓰신 글 :
: : : 안녕하세요
: : :
: : : 기존에 개발되어 사용하던 32bit프로그램을 64bit으로 변환하는 과정에서 발견 된 현상있어 문의 드립니다.
: : :
: : :
: : : 현제 개발IDE는 Rad XE3 Update2를 사용하고 있습니다.
: : :
: : : 멀티스레드에서 로컬파일 DB접근이 동시에 이루어 질 수 있는 부분 있어
: : : Critical Section을 이용하여 동시접근을 제한하여 사용해 왔었습니다.
: : : 각 스레드에서 Critical Section 진입 및 해지(Enter, Leave)가 짝이 맞게 되어 있어
: : : 정상적으로 각 스레드에서 접근하여 읽기 쓰기 작업이 이루어 지며,
: : : windows 32bit환경으로 빌드하여 프로그램 사용시 정상동작을 확인했습니다.
: : :
: : :
: : : 하지만, 동일 프로그램을 windows 64bit환경으로 변경하고, 빌드하여 사용시 문제가 있었습니다.
: : : 한 쓰레드에서 CriticalSection 진입(EnterCriticalSection)후 나오더라도(LeaveCriticalSection)
: : : 다른 쓰레드에서 CriticalSection 진입시 Block되어 대기하고 있는 현상이 발생되고 있습니다.(Logging으로 문제점을 찾음)
: : :
: : : 테스트로 MS 비쥬얼스튜디오로 멀티스레드에서 CriticalSection사용하는 64bit 프로그램을 간단히
: : : 만들어 테스트 해보았지만, 해당 증상이 발견되고 있지는 않고 있습니다.
: : : 이러한 문제가 RadStudio의 C++ builder가 64bit으로 오면서 컴파일러가 LLVM Clang기반으로 추가된 것(바뀐 것?)으로 알고 있습니다.
: : : 이와 관련해서 생긴현상 인지도 모르겠다는 가설로, 일단 LLVM Clang에서 관련자료를 찾는 중입니다.
: : :
: : :
: : : 한 줄 요약
: : : -> '64bit에서는 LLVM Clang을 이용한 컴파일러로 인해 LeaveCriticalSection 명령이 않먹는거 같다'입니다.
: : :
: : : 혹시 관련 내용을 알고 계신분이 있으시다면 조언 부탁드립니다.
: : :
: :
: : EnterCriticalSection, LeaveCriticalSection 등은 윈도우즈 API에 불과하고 vc든 빌더 bcc64든 테스트 해보면 정상적으로
: : 문제 없이 실행 됩니다. api나 clang 컴파일러의 문제는 아니라는 거죠.
: :
: : EnterCriticalSection API는 이미 다른 쓰레드에 의해서 Ownership이 결정된 상태라면 Blocking 되지만
: : 같은 쓰레드에 의해서 호출될 때는 복수 호출되는것을 허용하고 있습니다. 그래야 데드락 현상을 피할 수 있으니까요.
: : 같은 쓰레드에 의해서 복수 호출된 횟수 만큼 LeaveCriticalSection도 그 만큼 호출되어야 하고요. 내부적으로 카운트를
: : 유지하고 있다는 거죠.
: :
: : 이런 식으로 같은 쓰레드에서 복수 호출되는 상황의 경우에는 EnterCriticalSection으로 진입해서 LeaveCriticalSection 로
: : 나왔다 하더라도 내부 카운트가 0이 아니면 쓰레드 Owner는 Release된 상태가 아니라서 다른 쓰레드에 의해서 호출될 때
: : 여전히 Blocking 상태가 될 수 있습니다.
: :
: : 추측컨데 컴파일러의 문제라기 보단 프로그램 로직에 어떤 문제가 있는게 아닐까 생각되는데요
: :
: : 프로그램 로직을 찬찬히 다시 살펴 보세요. 32비트와 64비트 각기 다른 환경에서 발생하는 차이로 인한
: : 그게 포인터 일수도 있고 vcl 버그 등이 프로그램 로직과 물려서 어떤 문제를 내포하고 있을 가능성이 더 커 보이네요.
: :
: :
:
:
: 답변감사합니다.
:
: 일단 저도 CriticalSection 윈도우즈 API에 대해서는 의심하는 것은 아님니다.
: (동일 로직을 가지고 VC로 32bit, 64bit 모두 테스트 시 이상이 없기 때문)
:
: 말씀해 주신것 처럼 VCL의 문제나, 저의 프로그램 로직으로 인한 문제부분을 배제하고 있는것은 아님니다.
: 하지만 증상이, BCC32에서는 없는 현상이, BCC64에서만 나타나고 있어서 의심을 했습니다.
:
:
: 혹시나 제 머리가 굳어서 인가 하는 마음에 프로그램의 대략적인 로직도 그려 봄니다.
:
:
:
:
:
: DB를 접근하는 DB Unit은 싱글톤 형식으로 1개의 Obj를 통해서 모든 접근이 이루어 지고
: 각 스레드 들에서는 상태값 조회 및 변경을 하기 위해서, DB Unit의 읽기 또는 쓰기 메소드를 통해서 접근이 이루어 지고 있습니다.
: 또한, 스레드 내에서는 DB읽기 메소드 호출 작업이 완료 후, 로직을 타고, 다시 DB쓰는 메소드 호출 하는 식 이라서
: 말씀 하신 것처럼 쓰레드 내부에서 Critical Section의 복수의 호출은 없습니다.
:
: 혹 모르니 프로그램 로직도 다시한번 살펴봐야 겠습니다.
그런걸 디버깅 할 때는 디버깅 로직을 따로 만드세요.
EnterCriticalSection, LeaveCriticalSection API 를 직접 쓰지 말고 래퍼 클래스를 만들어서
class CMySync
{
void enter(...);
void leave(...);
....
~CMySync();
};
your logic...
.....
mySync.enter(curThreadHandle, __FILE__, __LINE__);
.....
.....
mySync.leave(curThreadHandle, __FILE__, __LINE__);
CMySync 클래스를
enter()가 호출 될 때, 쓰레드핸들과 파일명, 라인넘버 정보가 포함될 수 있도록 자료구조를 만들어서 넣고
leave()가 호출 될 때 enter()에서 입력된 정보인 쓰레드핸들과 일치하는 엔트리는 자료구조에서 빼고
프로그램 종료시
CMySync 소멸자에서 해제되지 않은 엔트리들을 쓰레드핸들, 파일명, 소스라인 포함해서 덤프하도록 클래스를 만들어서
테스트 하세요.
프로그램 개발 할 때 특히 Synchronization 같은 문제는 단순히 로그에 의존하지 않고 저런식으로 래퍼클래스를 만들어서
디자인하는 건 기본 중에 기본 입니다.