2017-10-23 13 views
3

많은 스레드가 0에 액세스 할 수있는 (동시에 공유 리소스가 없음) 기능이 있습니다 (함수 A라고 함). 주어진 시간에 사용자는 프로세스를 중지하는 데 사용할 수 있습니다. 중지 기능은 정상 종료가 수행 될 수 있도록 함수 A에 액세스하는 스레드가 있는지 확인해야합니다. 그렇게하는 기본 절차가 있습니까?메소드와 정지 기능 간의 올바른 동기화 방법

내가 할 일은 함수 A가 호출 될 때마다 정수 (InterlockedIncrement)를 호출하는 것입니다 (함수 A가있을 때 정수에 해당하는 InterlockedDecrement). InterlockedDecrement가 발생하면 정수 값을 검사하고, 0으로 설정하면 이벤트가 신호로 설정됩니다. 값이 0이 아니면 이벤트는 nonsignalled로 설정됩니다.

이것은 내 마음에 들지 만, 그렇게하기에 더 적합한 기본 구조/기능이 있는지 궁금합니다.

나는 "정지"기능이 굶어 죽을지도 모른다는 사실에 대해서는 여전히 고심해야한다. (의미 상, 상기 정수는 결코 0으로 설정 될 수 없다). A sidenote : 정지 이벤트가 발생하면 InterlockedIncrement 프로세스가 중지되어 상기 기아를 줄입니다.

답변

4

당신이 필요로하고 구현하고 싶은 것을 Run-Down Protection이라고합니다. 불행히도 커널 모드에서만 지원되지만 사용자 모드에서도 직접 구현하지는 않습니다.

HANDLE ghStopEvent; 
LONG gLockCount = 1; 
BOOLEAN bStop = FALSE; 

void unlock() 
{ 
    if (!InterlockedDecrement(&gLockCount)) SetEvent(ghStopEvent); 
} 

BOOL lock() 
{ 
    LONG Value = gLockCount, NewValue; 

    for (; !bStop && Value; Value = NewValue) 
    { 
     NewValue = InterlockedCompareExchange(&gLockCount, Value + 1, Value); 

     if (NewValue == Value) return TRUE; 
    } 

    return FALSE; 
} 

void funcA(); 

void UseA() 
{ 
    if (lock()) 
    { 
     funcA(); 
     unlock(); 
    } 
} 

하고 런 다운을 시작하고자 할 때 - 당신이 있지만 경우에만 1 lock 기능이 연동되어 증가 gLockCount을 볼 수있는 방법을 한 번

bStop = TRUE; unlock();

전화 :

간단한 구현 옆에 0이 아닙니다.

커널 모드에서는를 대신 호출 할 수 있습니다.

EX_RUNDOWN_REF gRunRef; 

void UseA() 
{ 
    if (ExAcquireRundownProtection(&gRunRef)) 
    { 
     funcA(); 
     ExReleaseRundownProtection(&gRunRef) 
    } 
} 

최종 장소에unlock-ExWaitForRundownProtectionRelease


런 다운 보호의 좀 더 복잡하고 확장 성 구현 :

#define RUNDOWN_INIT_VALUE 0x80000000 
#define RUNDOWN_COMPLETE_VALUE 0 

class __declspec(novtable) RUNDOWN_REF 
{ 
    LONG _LockCount; 

protected: 

    virtual void RundownCompleted() = 0; 

public: 

    BOOL IsRundownBegin() 
    { 
     return 0 <= _LockCount; 
    } 

    void Reinit() 
    { 
     if (InterlockedCompareExchange(&_LockCount, RUNDOWN_INIT_VALUE, RUNDOWN_COMPLETE_VALUE) != RUNDOWN_COMPLETE_VALUE) 
     { 
      __debugbreak(); 
     } 
    } 

    RUNDOWN_REF() 
    { 
     _LockCount = RUNDOWN_INIT_VALUE; 
    } 

    BOOL AcquireRundownProtection() 
    { 
     LONG Value = _LockCount, NewValue; 

     for (; Value < 0; Value = NewValue) 
     { 
      NewValue = InterlockedCompareExchange(&_LockCount, Value + 1, Value); 

      if (NewValue == Value) return TRUE; 
     } 

     return FALSE; 
    } 

    void ReleaseRundownProtection() 
    { 
     if (RUNDOWN_COMPLETE_VALUE == InterlockedDecrement(&_LockCount)) 
     { 
      RundownCompleted(); 
     } 
    } 

    void BeginRundown() 
    { 
     if (AcquireRundownProtection()) 
     { 
      _interlockedbittestandreset(&_LockCount, 31); 
      ReleaseRundownProtection(); 
     } 
    } 
}; 

및 사용은 좋아 :

class MY_RUNDOWN_REF : public RUNDOWN_REF 
{ 
    HANDLE _hEvent; 

    virtual void RundownCompleted() 
    { 
     SetEvent(_hEvent); 
    } 
    // ... 
} gRunRef; 


void UseA() 
{ 
    if (gRunRef.AcquireRundownProtection()) 
    { 
     funcA(); 
     gRunRef.ReleaseRundownProtection(); 
    } 
} 
,403,210 및 중지 할 때 : 커널에서 다른 하나의 존재

gRunRef.BeginRundown();// can be safe called multiple times 
// wait on gRunRef._hEvent here 

재미있는 것을 (더 오래된 - XP에서 보호를 소진 된 WIN2000에서) API를 Remove Locks. 그것은 거의 동일합니다. 내부 구현 및 사용법 만 다릅니다.

IO_REMOVE_LOCK gLock; 

void UseA() 
{ 
    if (0 <= IoAcquireRemoveLock(&gLock, 0)) 
    { 
     funcA(); 
     IoReleaseRemoveLock(&gLock, 0); 
    } 
} 

을 우리가 정지 할 때 - 가까운 잠금 구현, 두 번째 근처 개요 보호 구현을 제거 구현

IoAcquireRemoveLock(&gLock, 0); 
IoReleaseRemoveLockAndWait(&gLock, 0); 

내 첫 번째 코드 스피 넷 전화 : 제거 잠금 코드와 함께 같이 표시됩니다. 그러나 감각으로도 모두 똑같습니다.

+0

[인라인 함수] (https://docs.microsoft.com/en-us/cpp/cpp/inline-functions-cpp) : * "'__forceinline' 키워드는 비용을 무시합니다/benefit 분석을 수행하고 대신 프로그래머의 판단에 의존합니다. [...] '__forceinline'을 무차별 적으로 사용하면 한계 성능 향상만으로 더 큰 코드가 생성되거나 경우에 따라 성능 손실이 발생할 수 있습니다. 예를 들어 더 큰 실행 파일). "* – IInspectable

+0

@IInspectable - 좋아, 필자는 여기서 프로그래머의 판단을 선택한다. 하지만 어쨌든 인라인을 사용하거나 사용하지 않으면 여기에서 내 구현의 논리를 변경하지 마세요. – RbMm

+0

프로그래머의 판단이 잘못되었습니다. 멤버 함수는 이미 암시 적으로 인라인되어 있습니다. 이것은 * 무차별 적으로 사용하기 때문에 시각적 인 혼란을 야기하며 런타임 성능은'__forceinline'이없는 코드보다 좋거나 나쁘다. – IInspectable