C++0x has no semaphores? How to synchronize threads?

后端 未结 10 831
无人及你
无人及你 2020-11-22 07:18

Is it true that C++0x will come without semaphores? There are already some questions on Stack Overflow regarding the use of semaphores. I use them (posix semaphores) all the

相关标签:
10条回答
  • 2020-11-22 07:30

    in acordance with posix semaphores, I would add

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

    And I much prefer using a synchronisation mechanism at a convenient level of abstraction, rather than always copy pasting a stitched-together version using more basic operators.

    0 讨论(0)
  • 2020-11-22 07:30

    In case someone is interested in the atomic version, here is the implementation. The performance is expected better than the mutex & condition variable version.

    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};
    };
    
    0 讨论(0)
  • 2020-11-22 07:36

    You can easily build one from a mutex and a condition variable:

    #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;
        }
    };
    
    0 讨论(0)
  • 2020-11-22 07:36

    You can also check out cpp11-on-multicore - it has a portable and optimal semaphore implementation.

    The repository also contains other threading goodies that complement c++11 threading.

    0 讨论(0)
  • 2020-11-22 07:39

    I found the shared_ptr and weak_ptr, a long with a list, did the job I needed. My issue was, I had several clients wanting to interact with a host's internal data. Typically, the host updates the data on it's own, however, if a client requests it, the host needs to stop updating until no clients are accessing the host data. At the same time, a client could ask for exclusive access, so that no other clients, nor the host, could modify that host data.

    How I did this was, I created a struct:

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

    Each client would have a member of such:

    UpdateLock::ptr m_myLock;
    

    Then the host would have a weak_ptr member for exclusivity, and a list of weak_ptrs for non-exclusive locks:

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

    There is a function to enable locking, and another function to check if the host is locked:

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

    I test for locks in LockUpdate, IsUpdateLocked, and periodically in the host's Update routine. Testing for a lock is as simple as checking if the weak_ptr's expired, and removing any expired from the m_locks list (I only do this during the host update), I can check if the list is empty; at the same time, I get automatic unlocking when a client resets the shared_ptr they are hanging onto, which also happens when a client gets destroyed automatically.

    The over all effect is, since clients rarely need exclusivity (typically reserved for additions and deletions only), most of the time a request to LockUpdate( false ), that is to say non-exclusive, succeeds so long as (! m_exclusiveLock). And a LockUpdate( true ), a request for exclusivity, succeeds only when both (! m_exclusiveLock) and (m_locks.empty()).

    A queue could be added to mitigate between exclusive and non-exclusive locks, however, I have had no collisions thus far, so I intend to wait until that happens to add the solution (mostly so I have a real-world test condition).

    So far this is working well for my needs; I can imagine the need to expand this, and some issues that might arise over expanded use, however, this was quick to implement, and required very little custom code.

    0 讨论(0)
  • 2020-11-22 07:42

    I decided to write the most robust/generic C++11 semaphore I could, in the style of the standard as much as I could (note using semaphore = ..., you normally would just use the name semaphore similar to normally using string not basic_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();
    }
    
    0 讨论(0)
提交回复
热议问题