【发布时间】:2014-11-20 12:39:48
【问题描述】:
我需要为嵌入式设备制作自己的简单线程安全共享指针类。 我按照 Jeff Alger 的书(面向真正程序员的 C++)中的描述制作了计数主指针和句柄。这是我的消息来源:
template <class T>
class counting_ptr {
public:
counting_ptr() : m_pointee(new T), m_counter(0) {}
counting_ptr(const counting_ptr<T>& sptr) :m_pointee(new T(*(sptr.m_pointee))), m_counter(0) {}
~counting_ptr() {delete m_pointee;}
counting_ptr<T>& operator=(const counting_ptr<T>& sptr)
{
if (this == &sptr) return *this;
delete m_pointee;
m_pointee = new T(*(sptr.m_pointee));
return *this;
}
void grab() {m_counter++;}
void release()
{
if (m_counter > 0) m_counter--;
if (m_counter <= 0)
delete this;
}
T* operator->() const {return m_pointee;}
private:
T* m_pointee;
int m_counter;
};
template <class T>
class shared_ptr {
private:
counting_ptr<T>* m_pointee;
public:
shared_ptr() : m_pointee(new counting_ptr<T>()) { m_pointee->grab(); }
shared_ptr(counting_ptr<T>* a_pointee) : m_pointee(a_ptr) { m_pointee->grab(); }
shared_ptr(const shared_ptr<T>& a_src) : m_pointee(a_src.m_pointee) {m_pointee->grab(); }
~shared_ptr() { m_pointee->release(); }
shared_ptr<T>& operator=(const shared_ptr<T>& a_src)
{
if (this == &a_src) return *this;
if (m_pointee == a_src.m_pointee) return *this;
m_pointee->release();
m_pointee = a_src.m_pointee;
m_pointee->grab();
return *this;
}
counting_ptr<T>* operator->() const {return m_pointee;}
};
如果在一个线程中使用,这会很好。假设我有两个线程:
//thread 1
shared_ptr<T> p = some_global_shared_ptr;
//thread 2
some_global_shared_ptr = another_shared_ptr;
这种情况下,如果其中一个线程在内存分配/解除分配和计数器更改之间被中断,我会得到未定义的行为。当然,我可以将 shared_ptr::release() 包含在关键部分中,这样可以安全地删除指针。但是我可以用复制构造函数做什么?构造函数可能会在 m_pointee 构造期间被另一个线程中断,该线程将删除此 m_pointee。 我认为使 shared_ptr 分配线程安全的唯一方法是将分配(或创建)包含在关键部分中。但这必须在“用户代码”中完成。换句话说,shared_ptr 类的用户必须注意安全。 是否有可能以某种方式改变这种实现以使 shared_ptr 类线程安全?
=== 编辑 ===
经过一些调查(感谢 Jonathan),我意识到我的 shared_ptr 有三个不安全的地方:
- 非原子计数器更改
- 非原子赋值运算符(复制过程中可以删除源对象)
- shared_ptr 复制构造函数(与之前的案例非常相似)
前两种情况可以通过添加关键部分轻松解决。但是我不知道如何将临界区添加到复制构造函数中? a_src.m_pointee 的副本在构造函数中的任何其他代码执行之前创建,并且可以在调用 grab 之前删除。正如乔纳森在他的评论中所说,解决这个问题非常困难。
我做了这样的测试:
typedef shared_ptr<....> Ptr;
Ptr p1, p2;
//thread 1
while (true)
{
Ptr p;
p2 = p;
}
//thread 2
while (!stop)
{
p1 = p2;
Ptr P(p2);
}
当然,它崩溃了。但我尝试在 VS 2013 C++ 中使用 std::shared_ptr 。它有效!
因此可以为shared_ptr 创建线程安全的复制构造函数。但是 stl 来源对我来说太难了,我不明白他们是如何做到的。请任何人解释一下它在 STL 中是如何工作的?
=== 编辑 2 ===
对不起,std::shared_ptr 的测试出错了。它并没有像 boost::shared_ptr 那样完全通过。有时复制构造函数无法进行复制,因为在复制过程中源被删除。在这种情况下,将创建空指针。
【问题讨论】:
-
看
std::shared_ptr和boost::shared_ptr的实现。 -
你的计数必须是原子的!
-
你熟悉 C++11 中新的
atomic类型吗? -
@sjdowling,
shared_ptr确实不支持在不同步的情况下从同一个实例读取和写入。对some_global_shared_ptr的访问将是std::shared_ptr或boost::shared_ptr的未定义行为。 boost.org/doc/libs/1_57_0/libs/smart_ptr/… -
只是一个提示:尽量避免在不同线程之间共享数据,这是性能杀手。另见:kernel.org/pub/linux/kernel/people/paulmck/perfbook/…
标签: c++ multithreading pointers