2011-06-30 28 views
1

我有一些怀疑,这些mutexes足以确保下面的代码示例的线程安全性,或者如果需要原子。总之问题是:idxActive会使得这个代码线程不安全吗?或者,即使原子线程不安全的代码? :( 如果是重要的,我在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

这是不是属于上codereview.SE? –

+2

你知道你现在有std头''和''? –

+0

@ Kerrek SB-是的,旧习惯很难消逝。 – NoSenseEtAl

回答

3

你并不需要使用原子变量,如果所有访问这些变量由一个互斥保护。您的代码就是这种情况,因为所有公共成员函数在输入时锁定了addMutex。因此addIndex可以是一个普通的int,一切都会正常工作。互斥锁定和解锁确保正确的值对其他线程以正确的顺序可见。

std::atomic<>允许外部一个互斥锁的保护并发访问,确保线程看到变量的正确的价值观,甚至并发修改的面貌。如果您坚持默认的内存排序,它还可以确保每个线程读取变量值最新的std::atomic<>可以用来编写线程安全的算法不互斥,但如果所有的访问都是由同一个互斥保护不是必需的。

重要更新

我只注意到你正在使用互斥:一个用于addLog,一个用于printCurrent。在这种情况下,你需要idxActive是原子,因为单独互斥不提供他们之间的同步。

+0

我不想粗鲁,但你100%确定吗?因为就像我在给ChrisWue的评论中所说的,我不确定互斥确保更新的顺序,AFAIK它只确保当时的最大1线程执行该部分代码,而不是立即可见新值。我搜索了几乎没有发现的互斥体规范,以及我发现的 - 没有提及原子性。 – NoSenseEtAl

+2

是的,我100%确定。互斥量保证在锁定互斥锁时对共享变量所做的任何更改对于下一个锁定互斥锁的线程都是可见的。如果情况并非如此,那么它们的用处不大。 –

+0

好,很酷。再说一遍,这听起来很傲慢,但是你是否有任何“严重”的链接(标准,gcc文档......)指定了这一点。我尝试MSDNing我的搜索和C#互斥体没有运气来确认你的oppinion。顺便说一句,我的感觉是,你可能是对的,但当涉及到这样的东西,我想100%肯定, – NoSenseEtAl

1

atomic是没有直接关系的线程安全这只是确保了它的操作正是它说:。原子即使你所有的操作都是原子代码不一定是线程安全的

在你的情况下,代码应该是安全的,一次只能有一个线程可以输入printCurrent()当这个函数被执行时,其他线程可以调用addLog()(但也只有一个在tim E)。取决于switchCurrent是否已被执行,这些条目将使其进入当前日志,或者它们不会但在迭代它时不会输入任何内容。每次只有1个线程可以输入addLog,它与switchCurrent共享其互斥量,因此它们不能同时执行。

这会是这样的,即使你做 idxActive一个简单的INT MH,C++的内存模型只与单线程代码交易 - 所以我也不太清楚,如果理论上可以打破它。我认为如果你让idxActive变得不稳定(基本上根本不允许任何加载/存储优化),那么对于所有实际的目的它都可以。或者,您可以从switchCurrent中删除互斥锁,但您需要保持原子的idxActive

作为改善我先给switchCurrent返回旧指数,而不是重新计算它的。

+0

“即使您将idxActive设置为简单的int,情况也会如此。”但不能说不同的核心可以在缓存或寄存器中拥有不同版本的idxActive?因此,尽管互斥体保证只有一个人可以进入临界区使得idxActive正则int可能会导致线程填充错误的数据向量?或者我错了? – NoSenseEtAl

+0

@NoSenseEaAl:好点,更新我的回答 – ChrisWue