我已经实现了Boost等效于strand(boost :: asio)。由于超出此问题范围的原因,无法使用boost实现。
我的原始实现使用std :: mutex。在进行一些性能分析后,这个原始实现已成为一个热点 - 即需要从用户/内核空间转换(因为互斥)。
鉴于此,我尝试使用TBB的concurrent_queue编写无锁实现。尽管如此,我认为存在竞争条件,因为看起来在运行几分钟后,工作停止提交(我相信有工作排队)。现在,我的代码中可能存在其他问题。
我一直在质疑是否有可能没有互斥锁,因为有两个条件需要检查:
非常感谢任何帮助。
#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;
};
环境: