2009-04-26 8 views
14

저는 C++을 처음 접했고 다른 작성자가 스택에 객체를 푸시하고 스택에서 객체를 끌어 당기는 (또는 적어도 객체에 포인터를 밀어 넣는) 멀티 스레드 응용 프로그램을 작성했습니다. ..스레드 안전 C++ 스택

잠금 코드 등을 추가하지 않고도 처리 할 수있는 C++ 구조가 있습니까? 그렇지 않다면, Boost 라이브러리는 어떻습니까?

편집 :

안녕하세요. 초기 좋은 답변 주셔서 감사합니다. 내가 생각할 수있는 이유 중 하나는 x86 공간에서 순수하게 생각하고 포인터의 PUSH/POP가 명령어 수준의 원자 적 동작이어야한다고 생각했기 때문입니다.

나는 처음의 직감이 사실인지 아닌지 잘 모르겠지만 모든 플랫폼에서 반드시 그런 것은 아니 겠지. x86에서 실행 중이면 스택에 원자 PUSH 및 POP를 가져 오겠습니까? 그렇다면 본질적으로 잠금을 해제합니까?

+0

x86 PUSH/POP 명령어의 원 자성에 관심이 있다면 별도의 질문을하십시오. 스택 데이터 구조에 액세스하는 데 이러한 명령어를 사용하지 않는 C++와는 아무런 관련이 없습니다. –

+2

위원회는 TR1에서 의무적으로 사용되는 컴파일러에 대한 원자 모델 및 훨씬 더 나은 메모리 모델 추상화 (TR2에서도 아닐 수도 있음)보다는 DDJ에서 병렬 설교 수업을 작성하는 것이 더 바쁩니다. 답변 : 당신은 정말로 밀어 붙이거나 팝하지 않아서 스레드 간 레지스터를 암시 적으로 수정하지 않고 현재 별개의 코어에서 실행 중이다라고합니까? :-) 좋은 샷이지만 작동하지 않습니다. CAS 잠금 장치를 사용하지 않거나 적어도 CAS 망치를 사용할 수는 없습니다. C++ zealots의 경우 : 그들은 단지 앉아서 기존 일관성 프로토콜을 정의하고 동의해야하며 새로운 개발을위한 일부 범위를 남겨 두어야합니다. –

+0

관심이있는 사용자는 원자 작업을 조사하고 cmpxchg16b를 통해 DCAS를 지원합니다. 불행히도 AMD에는 cmpxchg8b 만 있습니다. 내가 Intel 머신 용으로 작성한 이래로 중요하지 않다. – bugmenot77

답변

21

옙 : Boost.Thread은 훌륭하며 사용자의 요구에 잘 부합해야합니다. (요즘에는 많은 사람들이 Boost를 빌트인 기능으로 간주 할 수 있다고 말합니다.)

즉시 사용할 수있는 클래스는 없지만 일단 동기화 프리미티브를 얻은 후에는 실제로는 스레드 안전 래퍼를 구현하는 것이 매우 간단합니다 (예 : std::stack). 그것은 (하지 ... 모든 메소드를 구현)과 같이 보일 수 있습니다 : 당신이 C++를 처음 사용하는 경우에 대한 RAII 내용하시기 바랍니다

template <typename T> class MyThreadSafeStack { 
    public: 
    void push(const T& item) { 
     boost::mutex::scoped_lock lock(m_mutex); 
     m_stack.push(item); 
    } 
    void pop() { 
     boost::mutex::scoped_lock lock(m_mutex); 
     m_stack.pop(); 
    } 
    T top() const { // note that we shouldn't return a reference, 
        // because another thread might pop() this 
        // object in the meanwhile 
     boost::mutex::scoped_lock lock(m_mutex); 
     return m_stack.top(); 
    } 

    private: 
    mutable boost::mutex m_mutex; 
    std::stack<T> m_stack; 
}  

. 이 경우 Boost.Thread에는 자물쇠를 놓는 것을 잊어서 다리에서 자신을 쏠 수 없게하는 "범위가 지정된 자물쇠"클래스가 있습니다. 혹시 자신과 같은 코드를 작성 찾을 경우

:

void doStuff() { 
    myLock.lock(); 
    if (!condition) { 
    reportError(); 
    myLock.unlock(); 
    return; 
    } 
    try { 
    doStuffThatMayThrow(); 
    } 
    catch (std::exception& e) { 
    myLock.unlock(); 
    throw e; 
    } 
    doMoreStuff(); 
    myLock.unlock(); 
} 

를, 당신은 '아니오'라고해야하고, (직접적으로 부스트에서 구문을) 대신 RAII를 이동 :

void doStuff() { 
    scoped_lock lock; 
    if (!condition) { 
    reportError(); 
    return; 
    } 
    doStuffThatMayThrow(); 
    doMoreStuff(); 
} 

점을 scoped_lock 객체가 범위를 벗어나면 소멸자가 리소스 (이 경우 잠금)를 해제한다는 것입니다. 이것은 예외를 던져서 스코프를 빠져 나갈 지 또는 동료가 당신의 함수 중간에 몰래 덧붙인 홀수 return 문장을 실행 하든지간에, 또는 단순히 함수의 끝까지 도달함으로써 항상 발생합니다.

+0

굉장한 대답. 고마워, 이것은 매우 명확한 답변과 매우 도움이되었다! – bugmenot77

+0

당신은 환영합니다; 내가 도울 수 있다면 기뻐. – Reunanen

+2

뮤텍스에 의해 잠겨져 있더라도 std 컨테이너는 스레드로부터 안전하지 않습니다. 그 이유는 수정을 통해 기존 반복자가 무효화되기 때문입니다. – ASk

4

현재 C++ 표준은 스레딩을 전혀 다루지 않으므로 첫 번째 질문에 대한 대답은 아니오입니다. 그리고 일반적으로 올바르게 그리고/또는 효율적으로 수행하기에 충분한 정보가 없으므로 기본 데이터 구조에 잠금을 설정하는 것은 나쁜 생각입니다. 대신 잠금은 데이터 구조를 사용하는 클래스에서 수행해야합니다. 즉, 자신의 응용 프로그램 클래스에서 수행해야합니다.

+1

나는 정렬 할 필요가 없다 (downvote는 아님). 나는 private std :: stack 인스턴스를 래핑하는 클래스 ThreadSafeStack을 작성하는 것이 완벽하다고 생각한다. 그런 다음 각 메소드 (푸시, 팝, ...)의 시작 부분에서 중요한 섹션 또는 이와 유사한 섹션을 입력하십시오. top()은 더 이상 참조가 아니라 복사본을 반환해야합니다. 이로 인해 효율성이 떨어지지 만 앱 클래스 작성의 편의성은 대개 가치가 있다고 생각합니다. 그렇지 않은 경우 (예 : 거대한 오브젝트가 복사 됨) 래퍼 클래스를 사용하지 마십시오. – Reunanen

+0

그래, 그게 내가 의미하는 바이지만 잘못 표현 된 것 같다. ThreadSafeStack은 애플리케이션 클래스 중 하나이다. 그러나 범용 라이브러리는 그러한 수업을 제공해서는 안됩니다 (IMHO). –

+1

아, 알았습니다. 나는 비즈니스 로직을 주로 포함하는 어플리케이션 클래스를 생각하고 있었다. – Reunanen

1

AFAIK, C++에서 지원하지 않습니다. 간단한 동기화 도구를 사용하여 스택 작업을 동기화해야합니다. CriticalSection은 스레드가 동일한 프로 시저에 속하고 뮤텍스로 이동하는 경우 수행됩니다.

1

C++이나 Boost 라이브러리 (참고 : 어떤 사람들은 thread-safe stacks/etc. in the Boost style을 작성했습니다)에서이를 지원하는 기본 제공 메커니즘이 없습니다. 당신은 약간의 코드를 빌리거나 자신의 동기화로 요리해야합니다.

아마도 여러 작성자 스레드가 스택에 액세스 할 수있는 단일 작성기 다중 판독기 가드 (SWMRG)가 필요하며 여러 독자가 해당 스택에 액세스 할 수있는 경우가 있습니다. 스택 (주어진 시점에서 많은). 리히터는 reference implementation입니다.

1

잠금을 사용하지 않으려면 자물쇠가없는 스택을 사용해야합니다. 이것은 실제로 그렇게 어렵지 않습니다 (잠금없는 큐는 더 어렵습니다). Windows에서 InterlockedCompareExchange와 같은 플랫폼 별 비교 교환 프리미티브가 필요하지만이를 추상화하는 것은 어렵지 않습니다.

는 C#에서 예를 들어 여기를 참조하십시오 : Windows에서 실행하는 경우

http://www.boyet.com/Articles/LockFreeRedux.html

0

, SLIST는 (구조 SLIST_HEADER & SLIST_ENTRY 포함) lockfree 스택을 구현합니다.

알고리즘은 연동 기능을 사용하여 매우 단순한 푸시/팝 단일 링크 목록 스택을 사용하여 구현됩니다. 명백하지 않은 유일한 항목은 ABA 문제를 피하기위한 카운터 증가입니다.

+0

감사합니다. 나는 이것이 다른 사람들에게 유용하기를 희망한다. Linux에서 실행 중이었고 위의 범위가 지정된 잠금 솔루션을 사용했습니다. 결국, 구조보다는 코드에 잠금 코드를 넣습니다. – bugmenot77