我有些疑问,互斥锁足以确保以下代码示例的线程安全性,或者是否需要原子。简而言之,问题是:使idxActive
成为常规int
会使此代码线程不安全吗?或者代码甚至与原子线程不安全? :(
如果它很重要,我就是32位x86,linux,gcc 4.6。当然我认为32或64位没有差异,但如果在32位和64位之间有任何差异,我想知道。
#include <memory>
#include <boost/thread/thread.hpp>
#include <string>
#include <vector>
#include <atomic>
#include <boost/thread/mutex.hpp>
using namespace std;
using namespace boost;
static const int N_DATA=2;
class Logger
{
vector<string> data[N_DATA];
atomic<int> idxActive;
mutex addMutex;
mutex printMutex;
public:
Logger()
{
idxActive=0;
for (auto& elem: data)
elem.reserve(1024);
}
private:
void switchDataUsed()
{
mutex::scoped_lock sl(addMutex);
idxActive.store( (idxActive.load()+1)%N_DATA );
}
public:
void addLog(const string& str)
{
mutex::scoped_lock sl(addMutex);
data[idxActive.load()].push_back(str);
}
void printCurrent()
{
mutex::scoped_lock sl(printMutex);
switchDataUsed();
auto idxOld=(idxActive.load()+N_DATA-1)%N_DATA; //modulo -1
for (auto& elem:data[idxOld])
cout<<elem<<endl;
data[idxOld].clear();
}
};
int main()
{
Logger log;
log.addLog(string("Hi"));
log.addLog(string("world"));
log.printCurrent();
log.addLog(string("Hi"));
log.addLog(string("again"));
log.printCurrent();
return 0;
}
答案 0 :(得分:3)
如果对这些变量的所有访问都受互斥锁保护,则不需要使用原子变量。在您的代码中就是这种情况,因为所有公共成员函数在进入时都会锁定addMutex
。因此addIndex
可以是普通的int
,一切都会正常。互斥锁定和解锁可确保正确的值以正确的顺序对其他线程可见。
std::atomic<>
允许并发访问外部保护互斥锁,确保线程看到变量的正确值,即使面对并发修改也是如此。如果您坚持默认的内存排序,它还可以确保每个线程读取变量的最新值。 std::atomic<>
可用于编写没有互斥锁的线程安全算法,但如果所有访问都受相同的互斥锁保护,则不需要{<1}}。
重要更新:
我刚注意到您使用的是两个互斥锁:一个用于addLog
,另一个用于printCurrent
。在这种情况下,执行需要idxActive
是原子的,因为单独的互斥锁不会在它们之间提供任何同步。
答案 1 :(得分:1)
atomic
与线程安全没有直接关系。它只是确保对它的操作正是它所说的:原子。即使您的所有操作都是原子操作,您的代码也不一定是线程安全的。
在您的情况下,代码应该是安全的。一次只能有一个帖子进入printCurrent()
。执行此功能时,其他线程可以调用addLog()
(但一次也只能调用1个)。根据{{1}}是否已经执行,这些条目将进入当前日志,否则它们将不会,但迭代它时将不会输入任何条目。一次只有一个线程可以输入switchCurrent
,它与addLog
共享其互斥锁,因此无法同时执行它们。
即使你使 Mh也是如此,C ++内存模型只处理单线程代码 - 所以我不太确定理论上是否它可以打破它。我认为如果你使switchCurrent
成为一个简单的int idxActive
易变(基本上不允许对其进行任何加载/存储优化),那么它可以用于所有实际目的。或者,您可以从idxActive
中删除互斥锁,但是您需要保持switchCurrent
原子。
作为改进,我会让idxActive
返回旧索引,而不是重新计算它。