IT

C ++ 0x에 세마포어가 없습니까?

lottoking 2020. 7. 11. 09:22
반응형

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을 잠글 수도 있습니다).

부스트에는 세마포어가 내부 위의 방법 중 가장 간단한 방법은 무엇입니까?


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

#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) 세마포어를 작성하기로 결정했습니다 (참고 : 일반적으로 하지 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 library는 복사 할 수 있는 간단한 예제 가 있습니다 (C ++ 0x 및 boost 라이브러리는 매우 유사합니다).


또한 유용한 기능 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();

목록이 긴 shared_ptr 및 weak_ptr이 필요한 작업을 수행했습니다. 내 문제는 호스트의 내부 데이터와 상호 작용하기를 여러 클라이언트가 여러 것입니다. 일반적으로 호스트는 자체적으로 데이터를 업데이트하지만 클라이언트가 요청하면 호스트는 호스트 데이터에 액세스하는 클라이언트가 없을 때 업데이트를 중지해야합니다. 독점 클라이언트는 다른 클라이언트 나 호스트가 해당 호스트 데이터를 독점 액세스를 수 있습니다.

이 작업을 수행 한 방법은 만들었습니다.

struct UpdateLock
{
    typedef std::shared_ptr< UpdateLock > ptr;
};

각 클라이언트에는 다음과 같은 구성원이 있습니다.

UpdateLock::ptr m_myLock;

그런 다음 호스트는 배타성을위한 weak_ptr 멤버와 비 배타적 잠금에 대한 weak_ptr 목록이 있습니다.

std::weak_ptr< UpdateLock > m_exclusiveLock;
std::list< std::weak_ptr< UpdateLock > > m_locks;

잠금을 활성화하는 기능과 호스트가 있는지 확인합니다.

UpdateLock::ptr LockUpdate( bool exclusive );       
bool IsUpdateLocked( bool exclusive ) const;

LockUpdate, IsUpdateLocked 및 호스트의 업데이트 루틴에서 주기적으로 잠금을 테스트합니다. 잠금 테스트는 weak_ptr의 종료 여부를 확인하고 m_locks 목록에서 종료 된 항목을 제거하는 것만 큼 간단합니다 (호스트 업데이트에서 만 수행). 목록이 비어 있는지 확인할 수 있습니다. 동시에 클라이언트가 매달린 shared_ptr을 계속하면 자동 잠금 해제가 발생하며 클라이언트가 자동으로 파괴 될 때도 발생합니다.

(일반적으로 추가 및 삭제 전용으로 예약 됨) 대부분의 경우 LockUpdate (false)에 대한 요청, 즉 비 독점적 요청은 (! m_exclusiveLock) 동안 성공합니다. 그리고 독점에 대한 요청 인 LockUpdate (true)는 (! m_exclusiveLock) 및 (m_locks.empty ()) 둘 다일 때만 성공합니다.

배타적 잠금과 비 독점 잠금 사이를 사용하기 위해 큐를 추가 할 수 있습니다. 지금까지 충돌이 가능합니다. (대부분 실제 테스트 조건이 있습니다.)

지금까지이 필요에 잘 맞습니다. 확장 할 수있는 몇 가지 문제를 상상할 수있는 구현이 빠르며 사용자 코드가 거의 필요하지 않습니다.


누군가가 원자 버전에 관심이 있다면 여기에 구현이 있습니다. 성능은 뮤텍스 및 조건 변수 버전보다 더 좋을 예상됩니다.

class semaphore_atomic
{
public:
    void notify() {
        count_.fetch_add(1, std::memory_order_release);
    }

    void wait() {
        while (true) {
            int count = count_.load(std::memory_order_relaxed);
            if (count > 0) {
                if (count_.compare_exchange_weak(count, count-1, std::memory_order_acq_rel, std::memory_order_relaxed)) {
                    break;
                }
            }
        }
    }

    bool try_wait() {
        int count = count_.load(std::memory_order_relaxed);
        if (count > 0) {
            if (count_.compare_exchange_strong(count, count-1, std::memory_order_acq_rel, std::memory_order_relaxed)) {
                return true;
            }
        }
        return false;
    }
private:
    std::atomic_int count_{0};
};

참고 URL : https://stackoverflow.com/questions/4792449/c0x-has-no-semaphores-how-to-synchronize-threads

반응형