C ++无锁链实现 - 竞争条件?

时间:2018-03-20 20:13:24

标签: c++ multithreading threadpool

我已经实现了Boost等效于strand(boost :: asio)。由于超出此问题范围的原因,无法使用boost实现。

我的原始实现使用std :: mutex。在进行一些性能分析后,这个原始实现已成为一个热点 - 即需要从用户/内核空间转换(因为互斥)。

鉴于此,我尝试使用TBB的concurrent_queue编写无锁实现。尽管如此,我认为存在竞争条件,因为看起来在运行几分钟后,工作停止提交(我相信有工作排队)。现在,我的代码中可能存在其他问题。

我一直在质疑是否有可能没有互斥锁,因为有两个条件需要检查:

  1. 州 - >运行?
  2. 可用的工作量/任务
  3. 非常感谢任何帮助。

    #include <assert>
    #include <cstdint>
    #include <utility>
    
    #include <tbb/concurrent_queue.h>
    
    
    namespace
    {
    
    ///////////////////////////////////////////////////////////////////////////
    constexpr bool is_background_service(const strand::service_policy policy)
    {
        return policy == strand::service_policy::SERVICE_BACKGROUND;
    }
    
    } // End anonymous ns
    
    ///////////////////////////////////////////////////////////////////////////
    class strand final
    {
    public:
    
        enum class service_policy
        {
            SERVICE_DEFAULT = 0,
            SERVICE_FOREGROUND = 1,
            SERVICE_BACKGROUND = 2,
        };
    
    private:
    
        typedef std::pair<std::function<void()>, service_policy> pair_type;
    
    public:
    
        strand() = delete;
    
        ~strand() = default;
    
        ////////////////////////////
        explicit strand(thread_pool pool, const service_policy default_policy) :
            m_pool(std::move(pool)),
            m_default_policy(default_policy == service_policy::SERVICE_DEFAULT ? service_policy::SERVICE_FOREGROUND : default_policy)
        {
            // What does a default policy here mean??
            assert(default_policy != service_policy::SERVICE_DEFAULT);
        }
    
        ////////////////////////////
        void post(const std::function<void()> &f, const service_policy policy)
        {
            CB_ASSERT_MSG(!!f, "Bad function!");
    
            pair_type pair = {f,policy};
    
            const bool bPost = (++m_nCount) == 1;
    
            if (bPost)
            {
                // Thread pool parameters:
                // 1. Work task
                // 2. True = background, false = foreground.
                m_pool.enqueue_async(
                    [this, pair{std::move(pair)}]() { execute(pair); },
                    is_background_service(get_scheduling_policy(policy)));
            }
            else
            {
                m_q.push(std::move(pair));
            }
        }
    
        ////////////////////////////
        void execute(const pair_type &pair)
        {
            try
            {
                if (BOOST_LIKELY(!!pair.first))
                {
                    pair.first();
                }
            }
            catch (...)
            {
                post_next();
                throw;
            }
    
            post_next();
        }
    
        ////////////////////////////
        void post_next()
        {
            bool bPost = (--m_nCount != 0);
    
            pair_type pair;
            if (bPost && m_q.try_pop(pair))
            {
                // Note: Even if this is a pool thread, we queue
                // this work because we aim to be fair (even if it comes
                // at the expense of performance).
    
                // Thread pool parameters:
                // 1. Work task
                // 2. True = background, false = foreground.
                m_pool.enqueue_async(
                    [this, pair{std::move(pair)}]() { execute(pair); },
                    is_background_service(get_scheduling_policy(pair.second)));
            }
        }
    
    private:
    
        ////////////////////////////
        service_policy get_scheduling_policy(const service_policy policy)
        {
            switch (policy)
            {
                case service_policy::SERVICE_DEFAULT:
                {
                    return m_default_policy;
                }
                default:
                {
                    return policy;
                }
            }
        }
    
        // Lock-free concurrent queue.
        tbb::concurrent_queue<pair_type> m_q;
    
        // Number of items in queue.
        std::atomic_size_t m_nCount{0};
    
        // Thread pool to which work is sent.
        thread_pool m_pool{};
    
        const service_policy m_default_policy;
    };
    

    环境:

    • Ubuntu 17.10
    • GCC 7.2
    • C ++ 14

0 个答案:

没有答案