2011-02-01 6 views
4

순서가 맞지 않은 재진입 호출에 대한 단순한 주장?

void prepare()와 void finish()는 다음과 같이 순차적으로 호출됩니다.

prepare(); 
<do something>; 
finish(); 
... 
prepare(); 
<do something>; 
finish(); 

간단히 말해서 실제로 호출되고 있음을 간단한 테스트로 암기하고 싶습니다. 이 방법은 응용 프로그램에서 동시 또는 비 순차적으로 호출되지 않습니다.

이 응용 프로그램은 단일 스레드 응용 프로그램입니다. 이것은 간단한 개발/테스트 온 전성 검사로서 이러한 함수가 순서대로 호출되고 어떤 이유로 든 동시에 호출되지 않음을 확인합니다. 또한 성능이 중요하기 때문에 이러한 어설 션/온 전성 검사는 생산 코드에서 생략해야합니다!

는이 작업과 같은 간단한 assert()를 사용합니까?

int test = 0; 

void prepare() { 
    assert(++test == 1); 

    . 
    . 
    . 
} 

void finish() { 
    assert(--test == 0); 

    . 
    . 
    . 
} 
+0

어떤 종류의 캐싱을 완화하기 위해 int 테스트에 volatile을 사용해야합니까? – chriskirk

+0

휘발성으로 자격이 부여 된 int 테스트에 대한 의견이 있습니까? 현재와 ​​같은 방식으로 레지스터에 캐시 할 수 있습니까? – chriskirk

+0

@Fred Nurk 정교하게 주시겠습니까? 이 특별한 프로세스는 새로운 객체의 인스턴스화를 포함하지 않으며 단순히 애플리케이션 내에서 발생하는 프로세스 일뿐입니다. – chriskirk

답변

1

귀하의 코드를 확인합니다.

중첩이 허용되지 않는 경우 bool 대신 int의 사용할 수 있습니다 : 당신은 할 수 있습니다

bool locked = false;; 

void prepare() { 
    assert(! locked); 
    locked = true; 
    ... 
} 

void finish() { 
    assert(locked); 
    locked = false; 
    ... 
} 
+0

좋은 점, 실제로는 중첩되지 않습니다. – chriskirk

+0

#if NDEBUG를 사용할 필요가 없으므로 단 한 번의 주장만으로 모든 것이 가능할 것이라고 생각합니다. 어떻게 생각해? – chriskirk

+0

@chriskirk :'finish '에서'assert ((locked =. locked) == true);를'준비하고'assert ((locked =! locked) == false); 할 수 있습니다. 어쨌든 나는'#if NDEBUG'를 사용하는 편이 낫다고 생각합니다. 제 생각에는이 방법을 이해하는 것이 더 쉽지만, 단지 맛의 문제 일뿐입니다. 나는 대부분의 상황에서'bool'을 사용하는 것이'int'를 사용하는 것보다 비용이 적게 듭니다. 그리고 코드 논리가 더 명확하다고 생각합니다. – peoro

3

여기에 경쟁 조건이있다 : prepare의 두 개의 동시 인스턴스가 다음, 다음 모두 1를 얻기 모두 레지스터에 그것을 증가, 같은 시간에 test의 값을 true을 얻을 수있는 비교를 할 수 있습니다.

volatilenot going to help입니다. 대신과 같이, test에 뮤텍스를 놓아야합니다 : 당신이 preparefinish 호출을 중첩 할 수 필요가없는

boost::mutex mtx; 
int test = 0; 

void prepare() 
{ 
    boost::mutex::scoped_try_lock lock(&mtx); 
    assert(lock.owns_lock()); 
    assert(test++ == 0); 
    // ... 
} 

void finish() 
{ 
    boost::mutex::scoped_try_lock lock(&mtx); 
    assert(lock.owns_lock()); 
    assert(--test == 0); 
} 
+0

휘발성 문제가 도움이 되나요? – chriskirk

+0

@chriskirk 아니야. –

+0

@chriskirk : 아니오, 여기에 설명 된 이유로 http://stackoverflow.com/questions/2484980/why-is-volatile-not-considered-useful-in-multithreaded-c-or-c-programming –

3

만족하는

int test = 0; 

#ifndef NDEBUG 
int test = 0; 
#endif 

에 변경하려면 "이 테스트와 관련된 모든 코드는 생략해야합니다. m 생산 ".

+0

''static int''를''test''로 만드는 것은 아마 최적화 된 것을 멀리하기에 충분할 것입니다. –

+0

@larsmans : 가능합니다. 일부 컴파일러는 사용하지 않으면 불평 할 수 있습니다. – Raedwald

3

당신은 아마 원하는 :

int test = 0; 

void prepare() { 
    // enter critical section 
    assert(test++ == 0); 

    . 
    . 
    . 
    // leave critical section 
} 

void finish() { 
    // enter critical section 
    assert(--test == 0); 

    . 
    . 
    . 
    // leave critical section 
} 
+0

은 좀 더 명료하고 우아 해 보입니다. – chriskirk

+0

이것은 관용적 인 C++ 레퍼런스 카운팅 관용구입니다. 하지만 동시성 문제를 해소하지는 않습니다. 어떤 플랫폼을 사용하고 계십니까? – ThomasMcLeod

+0

이 연습은 prepare() 및 finish()가 제대로 호출되는지 확인하기 위해 수행되고 있습니다. gcc 4.1.2를 사용하는 64 비트 Linux의 단일 스레드 응용 프로그램입니다. – chriskirk

1

당신은 왜 안 RAII를 사용하여 C의 ++를 사용하고 있기 때문에? 재진입 사용 여부를 확인해야하지만 RAII는 작업을 상당히 단순화합니다. larsmans' mutexRaedwald's elimination in NDEBUG과 결합 :

struct Frobber { 
    Frobber() { 
    assert(mtx.try_lock()); 
#ifndef NDEBUG 
    try { // in case prepare throws 
#endif 
     prepare(); 
#ifndef NDEBUG 
    } 
    catch (...) { 
     mtx.unlock(); 
     throw; 
    } 
#endif 
    } 

    void something(); 
    // And the other actions that can be performed between preparation and finishing. 

    ~Frobber() { 
    finish(); 
#ifndef NDEBUG 
    mtx.unlock(); 
#endif 
    } 

private: 
#ifndef NDEBUG 
    static boost::mutex mtx; 
#endif 

    Frobber(Frobber const&); // not defined; 0x: = delete 
    Frobber& operator=(Frobber const&); // not defined; 0x: = delete 
}; 
#ifndef NDEBUG 
boost::mutex Frobber::mtx; 
#endif 

void example() { 
    Frobber blah; // instead of prepare() 
    blah.something(); 
    // implicit finish() 
} 

예를 들어 내부, 당신은 단순히 는 첫 번째는 준비없이 일을 할 수 없습니다 수 있고, 마무리는 항상 예외가 발생 된 경우에도 발생합니다. NDEBUG에 대한

사이드 노트 : 당신이이 방법을 사용하는 경우,이 어설 위해 사용되는 방법과 반대로 모든 번역 단위 (이 정의에서 정의 할 수 있는지 항상 항상 정의되거나 하나가 에 정의되지 않은 수 있도록 다양한 포인트).당신이 class<do something>;를 넣으면

+0

죄송합니다,이 응용 프로그램은 매우 지연 시간에 민감하므로 RAII가 적절하지 않다고 언급하는 것을 잊었습니다. 제안 해 주셔서 감사 드리며 일반적인 응용 프로그램 시나리오 및 요구 사항에서 유용합니다. – chriskirk

+0

@chriskirk : 위의 코드가 대기 시간에 부정적인 영향을 미칠 것이라고 어떻게 생각하십니까? RAII가 아닌 경우와 비교해 볼 때 어떤 오버 헤드가 있습니까? –

1

당신은 모든 검사의 필요성을 줄일 수

그냥 생성자 호출 prepare와 소멸자 호출 finish 있습니다. 그런 다음 자동으로 적절히 호출됩니다.

동시성과 중첩 문제가 계속 적용됨에 유의하십시오. 중첩을 막으려면 계속 추적 할 수있는 일종의 전역 상태 (정적 클래스 멤버?)가 필요하며 두 개 이상에서 사용되는 경우 해당 카운터에 대한 스레드 액세스는 mutex로 보호되어야합니다.

또한 개인 operator new/delete을 만들어 누군가가 힙에 파일을 만들고 손상시키지 않도록 할 수 있습니다.

+0

new/delete 연산자는 힙에 누군가를 생성하는 것을 방지하지 않습니다. 때로는 사람들이 코드를 해독 할 수 있다고 받아들이면됩니다. –