[c++] C ++ 0x에 세마포어가 없습니까? 스레드를 동기화하는 방법?

C ++ 0x에 세마포어가없는 것이 사실입니까? 스택 오버플로에는 세마포어 사용과 관련하여 이미 몇 가지 질문이 있습니다. 스레드가 다른 스레드에서 일부 이벤트를 기다릴 수 있도록 항상 (posix 세마포어)를 사용합니다.

void thread0(...)
{
  doSomething0();

  event1.wait();

  ...
}

void thread1(...)
{
  doSomething1();

  event1.post();

  ...
}

내가 뮤텍스로 그렇게한다면 :

void thread0(...)
{
  doSomething0();

  event1.lock(); event1.unlock();

  ...
}

void thread1(...)
{
  event1.lock();

  doSomethingth1();

  event1.unlock();

  ...
}

문제 : 추악하고 thread1이 뮤텍스를 먼저 잠근다는 보장이 없습니다 (같은 스레드가 뮤텍스를 잠그고 잠금 해제해야한다면 thread0과 thread1이 시작되기 전에 event1을 잠글 수도 없습니다).

따라서 boost에는 세마포어가 없으므로 위의 가장 간단한 방법은 무엇입니까?



답변

뮤텍스와 조건 변수에서 하나를 쉽게 만들 수 있습니다.

#include <mutex>
#include <condition_variable>

class semaphore
{
private:
    std::mutex mutex_;
    std::condition_variable condition_;
    unsigned long count_ = 0; // Initialized as locked.

public:
    void notify() {
        std::lock_guard<decltype(mutex_)> lock(mutex_);
        ++count_;
        condition_.notify_one();
    }

    void wait() {
        std::unique_lock<decltype(mutex_)> lock(mutex_);
        while(!count_) // Handle spurious wake-ups.
            condition_.wait(lock);
        --count_;
    }

    bool try_wait() {
        std::lock_guard<decltype(mutex_)> lock(mutex_);
        if(count_) {
            --count_;
            return true;
        }
        return false;
    }
};


답변

Maxim Yegorushkin의 답변을 기반으로 C ++ 11 스타일로 예제를 만들려고했습니다.

#include <mutex>
#include <condition_variable>

class Semaphore {
public:
    Semaphore (int count_ = 0)
        : count(count_) {}

    inline void notify()
    {
        std::unique_lock<std::mutex> lock(mtx);
        count++;
        cv.notify_one();
    }

    inline void wait()
    {
        std::unique_lock<std::mutex> lock(mtx);

        while(count == 0){
            cv.wait(lock);
        }
        count--;
    }

private:
    std::mutex mtx;
    std::condition_variable cv;
    int count;
};


답변

나는 가능한 한 표준 스타일로 할 수있는 가장 강력하고 일반적인 C ++ 11 세마포어를 작성하기로 결정했습니다 ( 주로 보통 not using semaphore = ...사용하는 semaphore것과 비슷한 이름을 사용합니다 )stringbasic_string

template <typename Mutex, typename CondVar>
class basic_semaphore {
public:
    using native_handle_type = typename CondVar::native_handle_type;

    explicit basic_semaphore(size_t count = 0);
    basic_semaphore(const basic_semaphore&) = delete;
    basic_semaphore(basic_semaphore&&) = delete;
    basic_semaphore& operator=(const basic_semaphore&) = delete;
    basic_semaphore& operator=(basic_semaphore&&) = delete;

    void notify();
    void wait();
    bool try_wait();
    template<class Rep, class Period>
    bool wait_for(const std::chrono::duration<Rep, Period>& d);
    template<class Clock, class Duration>
    bool wait_until(const std::chrono::time_point<Clock, Duration>& t);

    native_handle_type native_handle();

private:
    Mutex   mMutex;
    CondVar mCv;
    size_t  mCount;
};

using semaphore = basic_semaphore<std::mutex, std::condition_variable>;

template <typename Mutex, typename CondVar>
basic_semaphore<Mutex, CondVar>::basic_semaphore(size_t count)
    : mCount{count}
{}

template <typename Mutex, typename CondVar>
void basic_semaphore<Mutex, CondVar>::notify() {
    std::lock_guard<Mutex> lock{mMutex};
    ++mCount;
    mCv.notify_one();
}

template <typename Mutex, typename CondVar>
void basic_semaphore<Mutex, CondVar>::wait() {
    std::unique_lock<Mutex> lock{mMutex};
    mCv.wait(lock, [&]{ return mCount > 0; });
    --mCount;
}

template <typename Mutex, typename CondVar>
bool basic_semaphore<Mutex, CondVar>::try_wait() {
    std::lock_guard<Mutex> lock{mMutex};

    if (mCount > 0) {
        --mCount;
        return true;
    }

    return false;
}

template <typename Mutex, typename CondVar>
template<class Rep, class Period>
bool basic_semaphore<Mutex, CondVar>::wait_for(const std::chrono::duration<Rep, Period>& d) {
    std::unique_lock<Mutex> lock{mMutex};
    auto finished = mCv.wait_for(lock, d, [&]{ return mCount > 0; });

    if (finished)
        --mCount;

    return finished;
}

template <typename Mutex, typename CondVar>
template<class Clock, class Duration>
bool basic_semaphore<Mutex, CondVar>::wait_until(const std::chrono::time_point<Clock, Duration>& t) {
    std::unique_lock<Mutex> lock{mMutex};
    auto finished = mCv.wait_until(lock, t, [&]{ return mCount > 0; });

    if (finished)
        --mCount;

    return finished;
}

template <typename Mutex, typename CondVar>
typename basic_semaphore<Mutex, CondVar>::native_handle_type basic_semaphore<Mutex, CondVar>::native_handle() {
    return mCv.native_handle();
}


답변

posix 세마포어에 따라

class semaphore
{
    ...
    bool trywait()
    {
        boost::mutex::scoped_lock lock(mutex_);
        if(count_)
        {
            --count_;
            return true;
        }
        else
        {
            return false;
        }
    }
};

그리고 더 기본적인 연산자를 사용하여 스티칭 된 버전을 붙여 넣는 것보다 항상 편리한 추상화 수준에서 동기화 메커니즘을 사용하는 것이 좋습니다.


답변

cpp11-on-multicore를 확인할 수도 있습니다. 이식 가능하고 최적의 세마포어 구현이 있습니다.

이 저장소에는 c ++ 11 스레딩을 보완하는 다른 스레딩 기능도 포함되어 있습니다.


답변

뮤텍스 및 조건 변수로 작업 할 수 있습니다. 뮤텍스에 독점적으로 액세스하고, 계속 진행할 것인지 또는 다른 쪽 끝을 기다려야하는지 확인하십시오. 기다릴 필요가 있으면 조건을 기다립니다. 다른 스레드가 계속할 수 있다고 판단하면 조건을 알립니다.

boost :: thread 라이브러리에는 복사 할 수 있는 짧은 예제 가 있습니다 (C ++ 0x 및 boost thread 라이브러리는 매우 유사 함).


답변

스레드에서 유용한 RAII 세마포 랩퍼도 사용할 수 있습니다.

class ScopedSemaphore
{
public:
    explicit ScopedSemaphore(Semaphore& sem) : m_Semaphore(sem) { m_Semaphore.Wait(); }
    ScopedSemaphore(const ScopedSemaphore&) = delete;
    ~ScopedSemaphore() { m_Semaphore.Notify(); }

   ScopedSemaphore& operator=(const ScopedSemaphore&) = delete;

private:
    Semaphore& m_Semaphore;
};

멀티 스레드 앱의 사용 예 :

boost::ptr_vector<std::thread> threads;
Semaphore semaphore;

for (...)
{
    ...
    auto t = new std::thread([..., &semaphore]
    {
        ScopedSemaphore scopedSemaphore(semaphore);
        ...
    }
    );
    threads.push_back(t);
}

for (auto& t : threads)
    t.join();