C ++无锁队列崩溃多线程

时间:2017-10-27 13:10:43

标签: c++ multithreading atomic memory-fences

我试图在编写多个线程时更好地理解控制内存顺序。我过去经常使用互斥锁来序列化变量访问,但我试图避免那些可能提高性能的东西。

我有一个指针队列,可能被许多线程填充并被许多线程占用。它可以在单个线程中正常工作,但在使用多个线程运行时会崩溃。看起来消费者可能会获得指针的重复,导致它们被释放两次。这有点难以分辨,因为当我输入任何打印语句时,它运行良好而不会崩溃。

首先,我使用预先分配的向量来保存指针。我保留3个原子索引变量来跟踪向量中需要处理的元素。值得注意的是,我尝试使用_queue类型,其中元素本身是原子的,似乎没有帮助。这是更简单的版本:

std::atomic<uint32_t> iread;
std::atomic<uint32_t> iwrite;
std::atomic<uint32_t> iend;
std::vector<JEvent*> _queue;

// Write to _queue (may be thread 1,2,3,...)
while(!_done){
    uint32_t idx = iwrite.load();
    uint32_t inext = (idx+1)%_queue.size();
    if( inext == iread.load() ) return kQUEUE_FULL;
    if( iwrite.compare_exchange_weak(idx, inext) ){
        _queue[idx] = jevent; // jevent is JEvent* passed into this method
        while( !_done ){
            if( iend.compare_exchange_weak(idx, inext) ) break;
        }
        break;
    }
}

来自同一个班级

// Read from _queue (may be thread 1,2,3,...)
while(!_done){
    uint32_t idx = iread.load();
    if(idx == iend.load()) return NULL;
    JEvent *Event = _queue[idx];
    uint32_t inext = (idx+1)%_queue.size();
    if( iread.compare_exchange_weak(idx, inext) ){
        _nevents_processed++;
        return Event;
    }
}

我应该强调,我真的很有兴趣了解为什么这不起作用。实现一些其他预先制作的包会让我解决这个问题,但不会帮助我避免再次犯同样的错误。

更新 我认为Alexandr Konovalov的回答是正确的(请参阅我在下面的回答中的评论)。如果有人遇到此页面,则会更正&#34;写入&#34;部分是:

std::atomic<uint32_t> iread;
std::atomic<uint32_t> iwrite;
std::atomic<uint32_t> iend;
std::vector<JEvent*> _queue;

// Write to _queue (may be thread 1,2,3,...)
while(!_done){
    uint32_t idx = iwrite.load();
    uint32_t inext = (idx+1)%_queue.size();
    if( inext == iread.load() ) return kQUEUE_FULL;
    if( iwrite.compare_exchange_weak(idx, inext) ){
        _queue[idx] = jevent; // jevent is JEvent* passed into this method
        uint32_t save_idx = idx;
        while( !_done ){
            if( iend.compare_exchange_weak(idx, inext) ) break;
            idx = save_idx;
        }
        break;
    }
}

1 个答案:

答案 0 :(得分:1)

对我来说,当有2位作家和1位读者时,可能会出现一个问题。假设第一个作者在

之前停止
 _queue[0] = jevent;

和第二个写入器通过iend发出信号,表明其_queue [1]已准备好被读取。然后,读者通过iend看到_queue [0]已准备好被读取,所以我们有数据竞争。

我建议您尝试Relacy Race Detector,它非常适用于此类分析。