2017-12-01 3 views
2

소개 : 동기화를 들어, C#을 System.Threading.Monitor 클래스를 제공하는 스레드 동기화 루틴을 제공하는 등 Enter(), Exit(), TryEnter()와 비슷하게.에뮬레이션 C# 잠금 문 C++

private static readonly obj = new Object(); 

lock(obj) { 
    ... 
} 

문제점 :

는 또한, 정상적인 실행 흐름 또는 예외로 인해서, 잠금이 임계 코드 블록이 남아있는 경우 파괴 얻는다 확실하게 lock 문가 C에서 ++, 이를 위해 과 std::unique_lockMonitor 클래스에 적용되지 않고 Lockable 개념을 충족하는 유형에 적용됩니다. 그러나이 방법은 구문 적으로 C#이 여러 가지 이유로 구현 한 방법보다 약한 것으로 생각합니다.

재사용 할 수없는 변수 이름으로 로컬 범위를 오염시킵니다.

{ 
     std::unique_lock<std::mutex> lck{ mtx }; 
     ... 
    } 

등의 새로운 스코프를 추가하여이 문제를 해결할 수 있습니다.하지만이 표기법이 다소 어색한 것 같습니다. 무엇보다 저를 괴롭게하는 것은이 유효 C++입니다 :

std::unique_lock<std::mutex>{ mtx ]; // note there is no name to the lock! 
... 

그래서 잠금 보호에 적절한 이름을 잊어에 의해,이 문은하지 않고, 유형 std::unique_lock<std::mutex>의 "MTX"라는 이름의 변수 선언으로 해석됩니다 아무것도 잠겨있어!

C#에서 C#의 lock 문을 구현하려고합니다. C++ 17에서는이 아주 쉽게 수행 할 수 있습니다

#define LOCK(mutex) if(std::lock_guard<decltype(mutex)> My_Lock_{ mutex }; true) 

std::mutex mtx; 
LOCK(mtx) { 
    ... 
} 

Q : 어떻게 C++ 14분의 11 이것을에서 구현할 수 있습니까?

+0

주제가 없지만 지혜로운 단어 : 식별자 (이중 밑줄 포함) [모든 용도로 구현되어 있습니다] (http://eel.is/c++draft/lex.name#3). – StoryTeller

+0

@StoryTeller A'ight, 나는 그저 그 이름이 무엇과도 충돌하지 않을 정도로 불분명한지 확인하기를 원합니다. – Jodocus

+2

아마도 C#과 C++에 완전히 다른 언어가되어서 언어에 맞서지 말아야 할 것입니다. 나는'std :: unique_lock lock {mtx};는 매우 자연스럽지 만'private static readonly obj = new Object();'는 매우 복잡해 보인다. 왜 매크로가 필요하지 않니? :-) –

답변

1

는, 나는 다소 "해킹"임에도 불구하고, 실행 가능한 솔루션을 자신을 찾은 것 같아 :

template <typename T> 
struct Weird_lock final : private std::lock_guard<T> { 
    bool flip; 
    Weird_lock(T& m) : std::lock_guard<T>{ m }, flip{ true } { } 

    operator bool() noexcept { 
     bool old = flip; 
     flip = false; 
     return old; 
    } 
}; 

#define LOCK(mutex) for(Weird_lock<decltype(mutex)> W__l__{ mutex }; W__l__;) 

좋은 것은 그것이 결국 세미콜론이 필요하지 않습니다. 나쁜 것은 추가 bool을 필요로하지만, godbolt.org에서 볼 수 있듯이, 컴파일러는이를 어쨌든 최적화합니다.

2

따로 두는 여기에 방법, "당신은이 작업을 수행해야한다"는 세미콜론을 요구하기 때문에 그것은 매우 같은 아니지만 그것은 내가 그것을 제시 할 수있다 느낌을 충분히 근처에,

합니다. 이 순수 C++ (14) 솔루션은 기본적으로 그냥 바로 실행되는 람다 시작하는 매크로를 정의

template<typename MTX> 
struct my_lock_holder { 
    MTX& mtx; 
    my_lock_holder(MTX& m) : mtx{m} {} 
}; 

template<typename MTX, typename F> 
void operator+(my_lock_holder<MTX>&& h, F&& f) { 
    std::lock_guard<MTX> guard{h.mtx}; 
    std::forward<F>(f)(); 
} 

#define LOCK(mtx) my_lock_holder<decltype(mtx)>{mtx} + [&] 

my_lock_holder 그냥 나중에에 대한 뮤텍스 참조를 냅스를, 우리가 operator+를 오버로드 할 수 있습니다. 운영자가 경비를 만들고 람다를 실행한다는 아이디어입니다. 볼 수 있듯이 매크로는 기본 참조 캡처를 정의하므로 람다가 둘러싸는 범위의 모든 것을 참조 할 수 있습니다.

std::mutex mtx; 
LOCK(mtx) { 

}; // Note the semi-colon 

그리고 당신은 live 구축 볼 수 있습니다 : 그럼 거의 직선 앞으로합니다.

+0

이것은 확실히 유용하고 아주 영리합니다. 따라서 upvote입니다. 그것도 C++ 11로 할 수 있습니까? – Jodocus

+0

@Jodocus - 알다시피, 나는 C++ 14 특정 기능을 사용하지 않는다고 생각합니다. 나는 C++ 11 모드로 시도하지 않았다. 그것은 아주 잘 작동 할 수 있습니다. – StoryTeller

0

나는 당신이 제안 : 당신은 단순히 걱정하지 않는다 고유 변수 이름을 생성합니다 COUNTER 처리기 기호를 사용

#define UNIQUE_NAME(name) name##__COUNTER__ 
#define LOCK(mutex) std::lock_guard<decltype(mutex)> UNIQUE_NAME(My_Lock){ mutex }; 

.이야기꾼의 좋은 아이디어에서 영감을

+0

여기서 문제는 잠금 범위 끝에서 잠금이 해제되지 않지만 LOCK (뮤텍스) 문이 포함 된 범위가 남아있을 때입니다. – Jodocus