首页 文章

C 0x没有信号量?如何同步线程?

提问于
浏览
113

C 0x是否会没有信号量? Stack Overflow上已经有一些关于信号量使用的问题 . 我一直使用它们(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也没有信号量,实现上述目标的最简单方法是什么?

9 回答

  • 8

    根据posix信号量,我想补充一下

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

    而且我更喜欢在方便的抽象级别使用同步机制,而不是总是使用更基本的运算符复制粘贴拼接在一起的版本 .

  • 6

    您还可以查看cpp11-on-multicore - 它具有可移植的最佳信号量实现 .

    存储库还包含补充c 11线程的其他线程好东西 .

  • 35

    您可以使用互斥锁和条件变量 . 您可以使用互斥锁获得独占访问权限,检查是否要继续或需要等待另一端 . 如果你需要等待,你就等待 . 当另一个线程确定您可以继续时,它会发出条件信号 .

    boost :: thread库中有一个简短的example,你最有可能只是复制(C 0x和boost线程库非常相似) .

  • 14

    您可以使用互斥锁和条件变量轻松构建一个:

    #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;
        }
    };
    
  • 1

    基于Maxim Yegorushkin's answer,我尝试用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;
    };
    
  • 2

    我决定尽可能地以标准的风格编写最强大/通用的C 11信号量(注意 using semaphore = ... ,你通常只使用名称 semaphore ,类似于通常使用 string 而不是 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();
    }
    
  • 143

    也可以在线程中使用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();
    
  • 97

    我发现shared_ptr和weak_ptr,一个带有列表的long,完成了我需要的工作 . 我的问题是,我有几个客户希望与主机的内部数据进行交互 . 通常,主机会自行更新数据,但是,如果客户端请求数据,主机需要停止更新,直到没有客户端访问主机数据 . 同时,客户端可以请求独占访问权限,这样任何其他客户端和主机都无法修改该主机数据 .

    我是怎么做到的,我创建了一个结构:

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

    每个客户都有这样的成员:

    UpdateLock::ptr m_myLock;
    

    然后主机将拥有一个独占的weak_ptr成员,以及一个非独占锁的weak_ptrs列表:

    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中测试锁定,并在主机的Update例程中定期测试 . 测试锁定就像检查weak_ptr是否过期一样简单,并从m_locks列表中删除任何过期(我只在主机更新期间执行此操作),我可以检查列表是否为空;同时,当客户端重置挂起的shared_ptr时,我会自动解锁,当客户端被自动销毁时也会发生这种情况 .

    最重要的是,由于客户端很少需要排他性(通常只保留用于添加和删除),因此大多数情况下对LockUpdate(false)的请求(即非排他性)只要(!m_exclusiveLock)成功 . LockUpdate(true)是一种排他性请求,只有在(!m_exclusiveLock)和(m_locks.empty())都成功时才会成功 .

    可以添加一个队列来缓解独占锁和非独占锁,但是,到目前为止我没有碰撞,所以我打算等到发生这种情况时才添加解决方案(主要是因为我有一个真实的测试条件) .

    到目前为止,这对我的需求很有效;我可以想象扩展它的必要性,以及可能因扩展使用而出现的一些问题,但是,这很快实现,并且需要很少的自定义代码 .

  • -3

    如果有人对原子版感兴趣,这里是实现 . 预计性能优于互斥和条件变量版本 .

    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};
    };
    

相关问题