我是C ++的新手,我正在编写一个多线程应用程序,不同的编写器会将对象推入堆栈,读者将它们从堆栈中拉出来(或者至少将指针推到对象上)。
C ++中是否内置了可以在不添加锁定代码等的情况下处理此问题的结构?如果没有,那么Boost库呢?
编辑:
您好。感谢最初的好答案。我想我认为这可能是内置的一个原因是我纯粹在x86空间思考并且认为指针的PUSH / POP应该是指令级别的原子动作。
我不确定我最初的预感是否正确,但我想这并不一定适用于所有平台。虽然如果在x86上运行,你是否会将原子PUSH和POP放到堆栈中,如果是这样,这实际上是否使它无锁?
答案 0 :(得分:21)
是的:Boost.Thread非常好,应该非常适合您的需求。 (现在,很多人都说你几乎可以将Boost视为内置功能。)
仍然没有可以开箱即用的类,但是一旦掌握了同步原语,实现自己的线程安全包装就非常简单,例如,{{ 1}}。它可能看起来像这样(没有实现每个方法......):
std::stack
如果您不熟悉C ++,请了解RAII。与此案相关的是,Boost.Thread拥有“范围锁定”类,因为忘记释放锁定而难以在腿部射击。
如果您发现自己编写了这样的代码:
template <typename T> class MyThreadSafeStack {
public:
void push(const T& item) {
boost::mutex::scoped_lock lock(m_mutex);
m_stack.push(item);
}
void pop() {
boost::mutex::scoped_lock lock(m_mutex);
m_stack.pop();
}
T top() const { // note that we shouldn't return a reference,
// because another thread might pop() this
// object in the meanwhile
boost::mutex::scoped_lock lock(m_mutex);
return m_stack.top();
}
private:
mutable boost::mutex m_mutex;
std::stack<T> m_stack;
}
,那么你应该说不,转而使用RAII(语法不是直接来自Boost):
void doStuff() {
myLock.lock();
if (!condition) {
reportError();
myLock.unlock();
return;
}
try {
doStuffThatMayThrow();
}
catch (std::exception& e) {
myLock.unlock();
throw e;
}
doMoreStuff();
myLock.unlock();
}
关键是当void doStuff() {
scoped_lock lock;
if (!condition) {
reportError();
return;
}
doStuffThatMayThrow();
doMoreStuff();
}
对象超出范围时,它的析构函数会释放资源 - 在本例中为锁。无论您是通过抛出异常退出范围,还是执行您的同事在您的函数中间偷偷添加的奇怪的scoped_lock
语句,或者只是到达函数的末尾,这都会发生。
答案 1 :(得分:4)
当前的C ++标准根本没有解决线程问题,所以第一个问题的答案是否定的。一般而言,将锁定构建到基本数据结构中是一个坏主意,因为它们没有足够的信息来正确和/或有效地执行它。相反,应该在使用数据结构的类中执行锁定 - 换句话说,在您自己的应用程序类中执行。
答案 2 :(得分:1)
AFAIK,没有内置的C ++支持。您必须使用简单的同步工具同步堆栈操作。如果线程属于同一个进程,则CriticalSection会执行,否则为Mutex。
答案 3 :(得分:1)
在C ++和Boost库中没有内置机制来支持这一点(注意:有些人写了thread-safe stacks/etc. in the Boost style)。您必须借用一些代码或在自己的同步中烹饪。
请注意,您的情况可能需要单写入器多读取器保护(SWMRG),其中多个写入器线程可以访问堆栈(但在给定时间点只有一个)并且多个读取器可以访问堆栈(许多在给定的时间点)。里希特有reference implementation。
答案 4 :(得分:1)
如果您不想使用锁定,则需要使用无锁堆栈。这实际上并不那么难(无锁队列更难)。您确实需要特定于平台的比较交换原语,例如Windows上的InterlockedCompareExchange,但这并不难抽象。
请参阅此处以获取C#中的示例:
答案 5 :(得分:0)
如果您在Windows上运行,SLIST会实现一个无锁堆栈(结构为SLIST_HEADER & SLIST_ENTRY
)。
该算法使用互锁函数使用相当简单的推/弹单链表列表来实现。唯一不明显的项目是计数器增量以避免ABA问题。