심 형근



가입: 2002년 8월 19일
올린 글: 526

올리기EnterCriticalSection 안에서 쓰레드가 죽는다면?올려짐: 2008-01-02 18:13
인용과 함께 답변 이 게시물을 del.icio.us에 추가

코드:
DWORD ThreadFunc(LPVOID * pParam)
{
EnterCriticalSection();
DoSomething();
LeaveCriticalSection();
}
 
 
int main()
{
for(i<~~~)
CreateThread();
 
DoSometing();
 
CloseHandle();
return 0;
};


쓰레드 함수인 ThreadFunc 안에서 EnterCriticalSection 에 진입한 상태에서 
다른 쓰레드 혹은 어떤 코드에서 실수로 CloseHandle() 을 호출하여 쓰레드를 죽였다면?? 
LeaveCriticalSection() 을 호출 못했으니? 영영 쓰레드에 접근 못하는 것일까요? 

크리티컬 섹션의 경우 보통 동기화 생성자의 생성자/파괴자에서 EnterCriticalSection 과 
LeaveCriticalSection 을 호출시키는데, 혹시 쓰레드가 남의손에 의해서 강제로 죽어도 
생성자/호출자는 알아서 호출이 될까요?
_________________
gdocument@hotmail.com(MSN)
위로
사용자 정보 보기 쪽지 보내기 이메일 보내기 MSN 메신저
jacking



가입: 2002년 1월 9일
올린 글: 1035

올리기올려짐: 2008-01-02 18:16
인용과 함께 답변 이 게시물을 del.icio.us에 추가

http://blog.naver.c...p;logNo=140031656740 

예전에 제 블로그에 올린 글인데 말하신 상황이 되면 댕글리 크리티컬섹션 상태가 되어 무한 데드락에 빠진다고 합니다. 
이런것을 방지하시려면 mutex를 사용해야 됩니다.
_________________
MS MVP( VC++ ) 
Twitter : jacking75 
블로그 1: http://jacking.tistory.com 
블로그 2: http://blog.naver.com/jacking75 
스프링노트 : http://jacking.springnote.com
위로
사용자 정보 보기 쪽지 보내기
myevan



가입: 2003년 3월 4일
올린 글: 1314

올리기올려짐: 2008-01-02 19:04
인용과 함께 답변 이 게시물을 del.icio.us에 추가

코드:
#include <windows.h>
 
CRITICAL_SECTION cs;
 
struct FuncLock
{
    FuncLock()  {EnterCriticalSection(&cs);puts("enter");}
    ~FuncLock() {LeaveCriticalSection(&cs);puts("leave");}
};
 
void func1()
{
    FuncLock funcLock;
 
    puts("step 1");
 
    throw "test";
 
    puts("step 2");
}
 
int main()
{
    InitializeCriticalSection(&cs);
 
    try {
        func1();
    catch(const char* e) {
         
    }
 
    DeleteCriticalSection(&cs);
    return 0;
}


의 실행 결과를 보면... 

인용:

enter 
step 1 
leave 

이런 결과가 ~(-_-)~
_________________
빗자루네 http://www.myevan.net >_<b
위로
사용자 정보 보기 쪽지 보내기 글 올린이의 웹사이트 방문
zupet



가입: 2003년 5월 13일
올린 글: 2760
소속: EA Seoul Studio

올리기올려짐: 2008-01-02 21:58
인용과 함께 답변 이 게시물을 del.icio.us에 추가

myevan 씀:
코드:
void func1()
{
    FuncLock funcLock;
 
    puts("step 1");
 
    throw "test";
 
    puts("step 2");
}


겸손하고 예의바른 throw 는 stack 을 깨끗히 정리해 줍니다. 테스트를 해보시려면 __try() ~ __except() 로 묶은 다음 throw 대신 memset(NULL, 0, 10) 같은 함수를 호출해 줘야 원하는 테스트가 가능할 겁니다. 

코드:
#include <windows.h>
 
CRITICAL_SECTION cs;
 
struct FuncLock
{
       FuncLock()    {EnterCriticalSection(&cs);puts("enter");}
       ~FuncLock()  {LeaveCriticalSection(&cs);puts("leave");}
};
 
void func1()
{
       FuncLock funcLock;
 
       puts("step 1");
 
       memset(NULL, 0, 10);
 
       puts("step 2");
}
 
int main()
{
       InitializeCriticalSection(&cs);
 
       __try
       {
           func1();
       }
       __except(1)
       {
           puts("except!!");
       }
 
       DeleteCriticalSection(&cs);
       return 0;
}


실행결과: 

인용:
enter 
step 1 
except!! 
Press any key to continue . . .
위로
사용자 정보 보기 쪽지 보내기 MSN 메신저
mastercho



가입: 2004년 5월 9일
올린 글: 587

올리기올려짐: 2008-01-03 10:18
인용과 함께 답변 이 게시물을 del.icio.us에 추가

근데 질문자님이 뭔가 잘못 아셨는거 같은데 CloseHandle 로는 다른 쓰레드를 중지 시키지 못합니다 

Main에서 CloseHandle 한것은 , 그저 쓰레드에 접근하는 참조 카운팅 수를 줄이고 쓰레드를 제어 하지 못하게 
될뿐입니다 

그리고 창조된 쓰레드가 리턴되어진다해도 쓰레드 생성할때 리턴받은 쓰레드 헨들을 Close 하지 않는다면 
쓰레드에 할당된 여러 리소스가 해제 되지 않기때문에 , 보통 쓰레드를 생성하자 마자 CloseHandle를 
하곤 합니다
위로
사용자 정보 보기 쪽지 보내기 이메일 보내기
myevan



가입: 2003년 3월 4일
올린 글: 1314

올리기올려짐: 2008-01-04 11:48
인용과 함께 답변 이 게시물을 del.icio.us에 추가

zupet 씀:
myevan 씀:
코드:
void func1()
{
    FuncLock funcLock;
 
    puts("step 1");
 
    throw "test";
 
    puts("step 2");
}


겸손하고 예의바른 throw 는 stack 을 깨끗히 정리해 줍니다. 테스트를 해보시려면 __try() ~ __except() 로 묶은 다음 throw 대신 memset(NULL, 0, 10) 같은 함수를 호출해 줘야 원하는 테스트가 가능할 겁니다. 

코드:
#include <windows.h>
 
CRITICAL_SECTION cs;
 
struct FuncLock
{
       FuncLock()    {EnterCriticalSection(&cs);puts("enter");}
       ~FuncLock()  {LeaveCriticalSection(&cs);puts("leave");}
};
 
void func1()
{
       FuncLock funcLock;
 
       puts("step 1");
 
       memset(NULL, 0, 10);
 
       puts("step 2");
}
 
int main()
{
       InitializeCriticalSection(&cs);
 
       __try
       {
           func1();
       }
       __except(1)
       {
           puts("except!!");
       }
 
       DeleteCriticalSection(&cs);
       return 0;
}


실행결과: 

인용:
enter 
step 1 
except!! 
Press any key to continue . . .


웃; 무서운데요;; 오한이;; ㄷㄷㄷ
_________________
빗자루네 http://www.myevan.net >_<b
위로
사용자 정보 보기 쪽지 보내기 글 올린이의 웹사이트 방문
kyh9052



가입: 2005년 10월 1일
올린 글: 49

올리기throw 는 stack 을 깨끗히 정리해 주는 것이 어떤 행동인가요?올려짐: 2008-01-04 14:58
인용과 함께 답변 이 게시물을 del.icio.us에 추가

throw 는 stack 을 깨끗히 정리해 주는 것이 어떤 행동인가요? 

제가 생각하기엔 

결과 
인용:
enter 
step 1 
except!! 
Press any key to continue . . .


은 

InitializeCriticalSection(&cs); 
EnterCriticalSection(&cs); 
// stack 을 깨끗히 정리 
DeleteCriticalSection(&cs); 

이 순서대로 실행되어졌다고 생각하는데 

LeaveCriticalSection 되지않은 DeleteCriticalSection의 호출은 문제가 발생되지않나요? 

혹시 throw 가 stack 을 깨끗히 정리(?)하는 과정에서 이 문제가 해결되는 것인가요?
위로
사용자 정보 보기 쪽지 보내기
zupet



가입: 2003년 5월 13일
올린 글: 2760
소속: EA Seoul Studio

올리기Re: throw 는 stack 을 깨끗히 정리해 주는 것이 어떤 행동인가올려짐: 2008-01-04 15:33
인용과 함께 답변 이 게시물을 del.icio.us에 추가

LeaveCriticalSection(&cs); 가 실행되지 않았습니다. 위의 코드를 보면 ~FuncLock() 에서 LeaveCriticalSection() 와 "leave" 라는 문구를 뿌려주는데 이 부분이 실행되지 않았습니다. 제가 작성한 코드는 임의의 쓰레드가 실행 도중 어떠한 이유에서 terminate 될 경우 CS 에 LeaveCriticalSection() 가 호출되지 않을 수 있다는 것을 보여주기 위한 예제입니다.
위로
사용자 정보 보기 쪽지 보내기 MSN 메신저
cycherMoon



가입: 2007년 8월 1일
올린 글: 3

올리기단순 컨솔창의 출력이상으로 보입니다올려짐: 2008-01-05 17:01
인용과 함께 답변 이 게시물을 del.icio.us에 추가

코드:
#include <Windows.h>
#include <iostream>
 
CRITICAL_SECTION g_CS ;
struct FuncLock
{
   FuncLock()
   {
      EnterCriticalSection( &g_CS ) ; //puts( "FuncLock()" ) ;
      std::cout << "FuncLock()" << std::endl ;
   }
   ~FuncLock()
   {
      std::cout << "~FuncLock()" << std::endl ;
      LeaveCriticalSection( &g_CS ) ; puts"~FuncLock()" ) ;
      std::cout << "~FuncLock()" << std::endl ;
 
   }
};
 
void TestFunc0()
{
   FuncLock funcLock ;
 
   //std::cout << "Before Booming" << std::endl ;
   puts"Before Booming" ) ;
   memset( NULL , 0 , 10 ) ;
   std::cout << "After Boomed" << std::endl ;
   //puts( "After Boomed" ) ;
 
}
 
 
int main()
{
   InitializeCriticalSection( &g_CS ) ;
 
   __try
   {
      TestFunc0() ;
   }
   __except( 1 )
   {
      std::cout << "예외에 걸려들었스..." << std::endl ;
   }
 
   DeleteCriticalSection( &g_CS ) ;
 
   system"pause" ) ;
   return 0 ;
}


인용:

FuncLock() 
Before Booming 
~FuncLock() 
~FuncLock() 
~FuncLock() 
예외에 걸려들었스... 
계속하려면 아무 키나 누르십시오 



여러가지로 테스트 해보니 아무래도 컨솔출력에 문제인것 같습니다 
지역객체의 소멸자가 호출이 안된다면 C++언어적 차원의 모순 아닐까요? 
그리고 위와 같은 테크닉을 보통 Scoped Auto Lock이라고 어느 외국 게시판에서 이야기 한것 같은데... 
출처는 생각이 나지 않네요
위로
사용자 정보 보기 쪽지 보내기
zupet



가입: 2003년 5월 13일
올린 글: 2760
소속: EA Seoul Studio

올리기Re: 단순 컨솔창의 출력이상으로 보입니다올려짐: 2008-01-06 00:29
인용과 함께 답변 이 게시물을 del.icio.us에 추가

cycherMoon 씀:
여러가지로 테스트 해보니 아무래도 컨솔출력에 문제인것 같습니다 
지역객체의 소멸자가 호출이 안된다면 C++언어적 차원의 모순 아닐까요? 
그리고 위와 같은 테크닉을 보통 Scoped Auto Lock이라고 어느 외국 게시판에서 이야기 한것 같은데... 
출처는 생각이 나지 않네요


이상하네요. 저는 위의 코드를 긁어다 붙여서 실행해보니 아래와 같이 나왔습니다. 어디에 차이가 있는 걸까요? 

인용:
FuncLock() 
Before Booming 
예외에 걸려들었스... 
Press any key to continue . . .


어쨌거나 이건 '언어' 차원을 넘어서 WIN32 환경에서 제공해주는 stack 공간, 예외등과 밀접한 관계가 있습니다. 원래 질문이 Lock 이 걸린 상태에서 쓰레드를 강제로 죽였을때 어떻게 되는가? 라는 질문이었고 myevan 님이 언급하신 throw 를 던지는 환경은 쓰레드를 강제로 죽였을때와 다르다는 것을 보여주기 위해 예외를 잡아서 결과를 보여주는 코드를 작성한 것이죠. 

위의 경우들에서 파악할 수 있는 것은 throw를 사용하면 C++ 언어에서 이러한 예외 처리를 해주지만 제 경우 SEH를 사용했지만 TerminateThread 를 해도 마찬가지로 OS 차원에서 실행중인 코드를 강재로 종료할 경우 함수가 Stack 을 정상적으로 따라 올라가면서 객체 해지 코드를 실행해주지 못한다는 것이죠. 

C++의 throw 의 경우 엄밀히 말하면 goto exit; 와 같은 코드가 함수내에 생성되고 적절한 함수 종료 시점으로 이동한 다음 함수 끝에 숨겨진 소멸자를 호출해주지만 이러한 goto 를 실행할 수 있다는 것은 코드 어느 시점에 throw 가 불릴 수 있다는 것을 예측에서 C++ 에서 자동적으로 코드를 생성해 줬기 때문이죠. 아쉽게도 쓰레드를 실행중 강제로 종료시키면 이렇게 예의바른 종료는 기대할 수 없습니다. 

흠.. 자바나 .NET 플렛폼 위에 돌아가는 애들은 가능하지 않을까요? (이건 테스트 해봐야겠죠)
위로
사용자 정보 보기 쪽지 보내기 MSN 메신저
ascalon



가입: 2005년 5월 27일
올린 글: 152

올리기올려짐: 2008-01-06 01:59
인용과 함께 답변 이 게시물을 del.icio.us에 추가

MSVC 에서는 try/catch를 쓰면 C++ exception handling 이며 __try/__catch는 OS 차원에서 제공되는 SEH 입니다. 

MSVC의 exception handling 종류: http://msdn2.microsoft.com/en-us/library/x057540h.aspx 
SEH: http://msdn2.microsoft.com/en-us/library/swezty51.aspx 

SEH는 C++위해 디자인된 것이 아니며, SEH를 사용할 경우 지역 객체의 소멸은 아래 링크에 설명된 것과 같이 컴파일러 옵션에 따라 다르게 작동합니다. 
http://msdn2.microsoft.com/en-us/library/1deeycx5.aspx 

혹은 __finally를 사용한 Termination Handler에서 clean up을 할 수 있습니다. 

아래 페이지에 SEH관한 좋은 내용이 있습니다. SEH가 C++ exception handler에 비해 보다 강력하고 속도도 빠르다고 되어 있군요. 
http://www.gamedev.net/reference/articles/article1272.asp
위로
사용자 정보 보기 쪽지 보내기 이메일 보내기 글 올린이의 웹사이트 방문
zupet



가입: 2003년 5월 13일
올린 글: 2760
소속: EA Seoul Studio

올리기올려짐: 2008-01-06 12:14
인용과 함께 답변 이 게시물을 del.icio.us에 추가

평소에 /EHa 를 안써서 cycherMoon 님이 언급하신 방법은 생각하지 못했습니다. 좀 구차해 보이지만 그래도 본래 질문에서는 /EHa 이 적용되지 않는다는 것을 언급하고 싶어서 코드를 다시 작성해 봤습니다. T_T 

코드:
#include <Windows.h>
 
CRITICAL_SECTION g_CS;
struct FuncLock
{
    FuncLock()
    {
        EnterCriticalSection( &g_CS );
        puts"FuncLock()" );
    }
    ~FuncLock()
    {
        LeaveCriticalSection( &g_CS );
        puts"~FuncLock()" );
    }
};
 
DWORD WINAPI TestThread(LPVOID)
{
    FuncLock funcLock;
    puts"Before Booming" );
    while(true)
    {
        puts"After Boomed" );
        Sleep(100);
    }
    return 0;
}
 
 
int main()
{
    InitializeCriticalSection( &g_CS );
 
    DWORD threadId;
    HANDLE handle = CreateThread(NULL, 0, TestThread, NULL, 0, &threadId);
 
    Sleep(1000);
 
    TerminateThread(handle, 0);
    CloseHandle(handle);
    puts"Thread Stopped" );
 
    DeleteCriticalSection( &g_CS );
    system"pause" );
 
    return 0;
}


이번 코드는 /EHa 상태로 컴파일을 했지만 아래와 같이 "~FuncLock()"이 호출 안된 상태로 쓰레드가 종료되었습니다. 

인용:
FuncLock() 
Before Booming 
After Boomed 
After Boomed 
After Boomed 
After Boomed 
After Boomed 
After Boomed 
After Boomed 
After Boomed 
After Boomed 
After Boomed 
Thread Stopped 
Press any key to continue . . .


아예 방법이 없는건 아닐 것 같습니다. TherminateThread()를 호출하는게 아니라 SuspendThread() 를 호출한다음 CONTEXT를 받아와서 실행 상태를 변경해서 예외를 발생시키는 것은 가능할 듯 싶습니다. 그렇지만 썩 기분 좋은 방법은 아닐듯 싶네요. -_-a
위로
사용자 정보 보기 쪽지 보내기 MSN 메신저
심 형근



가입: 2002년 8월 19일
올린 글: 526

올리기올려짐: 2008-01-06 14:15
인용과 함께 답변 이 게시물을 del.icio.us에 추가

쓰레드를 여러개 생성할때 쓰레드 풀에서 쓰레드를 생성하는 
방식을 주로 사용하게 됩니다. 문제는 크리티컬 섹션을 걸고풀고 
하는 상태에서 어떤 사유에서든(운영체제의 버그이던, 아니면 다른 문제이던) 

크리티컬 섹션에 락이 걸린상태로, 1개의 쓰레드가 죽는다면 
다른 모든 쓰레드 역시 죽는 사태가 발생하게 됩니다. 

약간의 퍼포먼스 패널티를 감수하고 어떤극한 상황에서도 "파괴자" 호출이 
보증된다면, 남은 쓰레드가 그래도 런타임코드를 살릴수 있을지도 모를까 싶어서 질문을 올려봤는데, 

역시나 결국은 버그없는 코드를 만드는것 이외에는 방법이 없을것 같네요. 
(하지만 버그없는 코드는 있을수가 없다는 전설이.. 쿨럭~~)
_________________
gdocument@hotmail.com(MSN)
위로
사용자 정보 보기 쪽지 보내기 이메일 보내기 MSN 메신저
ducklmg



가입: 2004년 11월 8일
올린 글: 155

올리기올려짐: 2008-01-07 11:16
인용과 함께 답변 이 게시물을 del.icio.us에 추가

저 위의 jacking 님께서 말씀하신것 처럼 Mutex 커널 객체를 이용하시면 됩니다. 

mutex는 abandoned 상태가 있어서 자신을 락 건 쓰레드가 죽으면, abandoned 상태로 바뀌고 

다른 쓰레드는 무한 기다림에서 빠져나올 수 있게 됩니다. 

다만 퍼포먼스 패널티가 있겠죠... 

코드:
EnterCriticalSection()
{
      WaitForSingleObject(hMutex, INFINITE);
}
 
LeaveCriticalSection()
{
      ReleaseMutex(hMutex);
}

_________________
난 너를 만나기 위해 이 세상에 태어났어 
그러니 내 생활비는 네가 대 주어야만 해

반응형

+ Recent posts