【发布时间】:2010-07-07 05:06:44
【问题描述】:
这是我编写的并发队列,我计划在我正在编写的线程池中使用它。我想知道是否可以进行任何性能改进。 atomic_counter 贴在下面,如果你好奇的话!
#ifndef NS_CONCURRENT_QUEUE_HPP_INCLUDED
#define NS_CONCURRENT_QUEUE_HPP_INCLUDED
#include <ns/atomic_counter.hpp>
#include <boost/noncopyable.hpp>
#include <boost/smart_ptr/detail/spinlock.hpp>
#include <cassert>
#include <cstddef>
namespace ns {
template<typename T,
typename mutex_type = boost::detail::spinlock,
typename scoped_lock_type = typename mutex_type::scoped_lock>
class concurrent_queue : boost::noncopyable {
struct node {
node * link;
T const value;
explicit node(T const & source) : link(0), value(source) { }
};
node * m_front;
node * m_back;
atomic_counter m_counter;
mutex_type m_mutex;
public:
// types
typedef T value_type;
// construction
concurrent_queue() : m_front(0), m_mutex() { }
~concurrent_queue() { clear(); }
// capacity
std::size_t size() const { return m_counter; }
bool empty() const { return (m_counter == 0); }
// modifiers
void push(T const & source);
bool try_pop(T & destination);
void clear();
};
template<typename T, typename mutex_type, typename scoped_lock_type>
void concurrent_queue<T, mutex_type, scoped_lock_type>::push(T const & source) {
node * hold = new node(source);
scoped_lock_type lock(m_mutex);
if (empty())
m_front = hold;
else
m_back->link = hold;
m_back = hold;
++m_counter;
}
template<typename T, typename mutex_type, typename scoped_lock_type>
bool concurrent_queue<T, mutex_type, scoped_lock_type>::try_pop(T & destination) {
node const * hold;
{
scoped_lock_type lock(m_mutex);
if (empty())
return false;
hold = m_front;
if (m_front == m_back)
m_front = m_back = 0;
else
m_front = m_front->link;
--m_counter;
}
destination = hold->value;
delete hold;
return true;
}
template<typename T, typename mutex_type, typename scoped_lock_type>
void concurrent_queue<T, mutex_type, scoped_lock_type>::clear() {
node * hold;
{
scoped_lock_type lock(m_mutex);
hold = m_front;
m_front = 0;
m_back = 0;
m_counter = 0;
}
if (hold == 0)
return;
node * it;
while (hold != 0) {
it = hold;
hold = hold->link;
delete it;
}
}
}
#endif
atomic_counter.hpp
#ifndef NS_ATOMIC_COUNTER_HPP_INCLUDED
#define NS_ATOMIC_COUNTER_HPP_INCLUDED
#include <boost/interprocess/detail/atomic.hpp>
#include <boost/noncopyable.hpp>
namespace ns {
class atomic_counter : boost::noncopyable {
volatile boost::uint32_t m_count;
public:
explicit atomic_counter(boost::uint32_t value = 0) : m_count(value) { }
operator boost::uint32_t() const {
return boost::interprocess::detail::atomic_read32(const_cast<volatile boost::uint32_t *>(&m_count));
}
void operator=(boost::uint32_t value) {
boost::interprocess::detail::atomic_write32(&m_count, value);
}
void operator++() {
boost::interprocess::detail::atomic_inc32(&m_count);
}
void operator--() {
boost::interprocess::detail::atomic_dec32(&m_count);
}
};
}
#endif
【问题讨论】:
-
您有具体的问题要问,或者有什么需要解决的问题吗? SO 不是代码审查网站。
-
我进行了编辑,表明我对性能改进建议感兴趣。
-
您看过 Herb Sutter 的 DDJ 文章 (drdobbs.com/cpp/211601363) 吗?他讨论了一些性能改进(对推送和弹出使用不同的锁,为缓存行大小填充成员变量)。更一般地说,你为什么要实现自己的而不是使用,例如待定
concurrent_queue? -
try_pop中存在潜在的内存泄漏。如果复制构造函数抛出destination = hold->value,那么hold将永远不会被删除。解决方案是做std::queue所做的事情:访问和删除队列前面的项目的单独方法。或者(正如 Gunslinger47 所说)使用std::queue而不是重新发明它。 -
@Mike 感谢您发现潜在的泄漏。那将是赋值运算符,对吗?我正在考虑使用一种虚拟对象,其析构函数在
hold上调用delete,因此如果赋值运算符确实抛出,则该节点将在堆栈展开时被删除。关于使用单独的方法来访问和删除:这在并发线程中是不切实际的。
标签: c++ concurrency queue