Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/actionscript-3/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ c++;11对对象状态和永久递增索引使用原子的多读取器/多写入器队列_C++_Multithreading_C++11_Atomic - Fatal编程技术网

C++ c++;11对对象状态和永久递增索引使用原子的多读取器/多写入器队列

C++ c++;11对对象状态和永久递增索引使用原子的多读取器/多写入器队列,c++,multithreading,c++11,atomic,C++,Multithreading,C++11,Atomic,我使用原子和循环缓冲区来实现多读线程、多写线程对象池 这很难调查,因为插入代码会导致bug消失 模型 生产者(或写入线程)向环请求元素,以便“准备”元素。终止时,writer线程更改元素状态,以便读取器可以“使用”它。之后,该元素再次可用于写入 使用者(或读线程)向环请求一个对象,以便“读取”该对象。 “释放”对象后,对象处于状态::就绪状态,例如可供读卡器线程使用。 如果没有对象可用,它可能会失败,例如环中的下一个可用对象未处于state::Unused状态 两个类,元素和环 元素: 要写入

我使用原子和循环缓冲区来实现多读线程、多写线程对象池

这很难调查,因为插入代码会导致bug消失

模型

生产者(或写入线程)向
环请求
元素
,以便“准备”元素。终止时,writer线程更改元素状态,以便读取器可以“使用”它。之后,该元素再次可用于写入

使用者(或读线程)向环请求一个对象,以便“读取”该对象。 “释放”对象后,对象处于
状态::就绪状态,例如可供读卡器线程使用。
如果没有对象可用,它可能会失败,例如环中的下一个可用对象未处于
state::Unused
状态

两个类,
元素

元素

  • 要写入,写入线程必须成功地将
    \u state
    成员从
    state::Unused
    交换到
    state::LockForWrite
  • 完成后,writer线程将状态强制为
    state::Ready
    (它应该是唯一处理此元素的线程)
  • 要读取,rader线程必须成功地将
    \u state
    成员从
    state::Ready
    交换到
    state::LockForRead
  • 完成后,读卡器线程将状态强制为
    state::Unused
    (它应该是唯一处理此元素的线程)
总结:

  • 写入程序生命周期:
    state::Unused
    ->
    state::LockForWrite
    ->
    state::Ready
  • 读卡器生命周期:
    state::Ready
    ->
    state::LockForRead
    ->
    state::Unused
Ring

  • 具有
    元素的向量
    ,被视为循环缓冲区
  • std::原子读、写是用于通过以下方式访问元素的两个索引:
    
    • \u elems[\u write%\u elems.size()]
      对于编写器
    • \u elems[\u read%\u elems.size()]
      供读者阅读
当读卡器成功地
LockForRead
对象时,
\u read
索引将递增。 当写入程序成功地
锁定写入对象时,
\u write
索引将递增

main

我们向向量中添加了一些共享相同
环的写入线程和读取线程。每个线程只需尝试获取_read或_write元素,并在之后释放它们

基于
元素
转换,一切都应该正常,但可以观察到环在某个点被阻塞,就像是因为环中的某些元素处于状态
状态::就绪
,其上有一个
\u write%\u elems.size(),环中的某些元素处于状态
state::Unused
,其上有一个
\u read%\u elems.size()
索引Both=死锁

#include<atomic>
#include<vector>
#include<thread>
#include<iostream>
#include<cstdint>

typedef enum : int
{
    Unused, LockForWrite, Ready,  LockForRead
}state;

class Element
{
    std::atomic<state> _state;
public:
    Element():_state(Unused){ }

    // a reader need to successfully make the transition Ready => LockForRead
    bool lock_for_read() { state s = Ready; return _state.compare_exchange_strong(s, LockForRead); }
    void unlock_read() { state s = Unused; _state.store(s); }

    // a reader need to successfully make the transition Unused => LockForWrite
    bool lock_for_write() { state s = Unused; return _state.compare_exchange_strong(s, LockForWrite); }
    void unlock_write() { state s = Ready;  _state.store(s); }
};

class Ring
{
    std::vector<Element> _elems;
    std::atomic<int64_t> _read, _write;

public:
    Ring(size_t capacity)
        : _elems(capacity), _read(0), _write(0) {}

    Element * get_for_read() {
        Element * ret = &_elems[ _read.load() % _elems.size() ];
        if (!ret->lock_for_read()) // if success, the object belongs to the caller thread as reader
            return NULL;
        _read.fetch_add(1); // success! incr _read index 
        return ret;
    }
    Element * get_for_write() {
        Element * ret = &_elems[ _write.load() % _elems.size() ];
        if (!ret->lock_for_write())// if success, the object belongs to the caller thread as writer
            return NULL;
        _write.fetch_add(1); // success! incr _write index
        return ret;
    }
    void release_read(Element* e) { e->unlock_read();}
    void release_write(Element* e) { e->unlock_write();}
};

int main()
{

    const int capacity = 10; // easy to process modulo[![enter image description here][1]][1]

    std::atomic<bool> stop=false;

    Ring ring(capacity);

    std::function<void()> writer_job = [&]()
    {
        std::cout << "writer starting" << std::endl;
        Element * e;
        while (!stop)
        {
            if (!(e = ring.get_for_write())) 
                continue;
            // do some real writer job ...
            ring.release_write(e);
        }
    };
    std::function<void()> reader_job = [&]()
    {
        std::cout << "reader starting" << std::endl;
        Element * e;
        while (!stop)
        {
            if (!(e = ring.get_for_read())) 
                continue;
            // do some real reader job ...
            ring.release_read(e);
        }
    };

    int nb_writers = 1;
    int nb_readers = 2;

    std::vector<std::thread> threads;
    threads.reserve(nb_writers + nb_readers);

    std::cout << "adding writers" << std::endl;
    while (nb_writers--)
        threads.push_back(std::thread(writer_job));

    std::cout << "adding readers" << std::endl; 
    while (nb_readers--)
        threads.push_back(std::thread(reader_job));

    // wait user key press, halt in debugger after 1 or 2 seconds
    // in order to reproduce problem and watch ring
    std::cin.get();

    stop = true;

    std::cout << "waiting all threads...\n";
    for (auto & th : threads)
        th.join();

    std::cout << "end" << std::endl;
}
#包括
#包括
#包括
#包括
#包括
typedef枚举:int
{
未使用、锁定写入、就绪、锁定读取
}国家;
类元素
{
原子态;
公众:
元素():_状态(未使用){}
//读卡器需要成功地使转换就绪=>LockForRead
bool lock_for_read(){states s=Ready;返回_state.compare_exchange_strong(s,LockForRead);}
void unlock_read(){state s=Unused;_state.store;}
//读卡器需要成功地使转换未使用=>LockForWrite
bool lock_for_write(){states s=Unused;返回_state.compare_exchange_strong(s,LockForWrite);}
void unlock_write(){state s=Ready;_state.store;}
};
阶级戒指
{
标准:向量元素;
std::原子读,写;
公众:
环(尺寸和容量)
:_elems(容量),_read(0),_write(0){
元素*get_for_read(){
元素*ret=&_元素[_read.load()%_elems.size()];
if(!ret->lock_for_read())//如果成功,则对象属于作为读取器的调用线程
返回NULL;
_read.fetch_add(1);//成功!增加读取索引
返回ret;
}
元素*get_for_write(){
元素*ret=&_元素[_write.load()%_elems.size()];
if(!ret->lock_for_write())//如果成功,则对象属于作为writer的调用线程
返回NULL;
_write.fetch_add(1);//成功!增加写入索引
返回ret;
}
void release_read(元素*e){e->unlock_read();}
void release_write(元素*e){e->unlock_write();}
};
int main()
{
const int capacity=10;//易于处理模[![在此处输入图像描述][1][1]
标准::原子停止=假;
环(容量);
std::函数编写器_作业=[&]()
{

std::cout在两个共享计数器的增量(读取和写入)周围没有原子部分。 这对我来说很糟糕,你可以毫无意义地切换另一个元素

想象一下这个场景, 1名读者R1和1名作家W正在愉快地合作

读卡器2执行:Element*ret=&_elems[_read.load()%_elems.size()]; 然后被推离cpu

现在R1和W仍然在一起玩,所以_read和_write的位置现在是任意的W.r.t。R2指向的元素ret

现在,在某个时刻R2被调度,并且碰巧*ret_uu是可读的(同样可能,R1和W在块中循环了几次)

哎哟,正如你所看到的,我们将阅读它,并增加“_read”,但是_read与_ret没有关系。这会创建一种孔,未被读取的元素,但在_read索引之下

因此,制作关键部分以确保在sa中完成_read/_write的增量
class Element
{
    std::atomic<state> _state;
public:
    Element():_state(Unused){ }

    // a reader need to successfully make the transition Ready => LockForRead
    bool lock_for_read() {
        state s = Ready;
        return _state.compare_exchange_strong(s, LockForRead);
    }
    void abort_read() { _state = Ready; }
    void unlock_read() { state s = Unused; _state.store(s); }

    // a reader need to successfully make the transition Unused => LockForWrite
    bool lock_for_write() {
        state s = Unused;
        return _state.compare_exchange_strong(s, LockForWrite);
    }
    void abort_write() { _state = Unused; }
    void unlock_write() { state s = Ready;  _state.store(s); }
};

class Ring
{
    std::vector<Element> _elems;
    std::atomic<int64_t> _read, _write;

public:
    Ring(size_t capacity)
        : _elems(capacity), _read(0), _write(0) {}

    Element * get_for_read() {
        auto i = _read.load();
        Element * ret = &_elems[ i % _elems.size() ];
        if (ret->lock_for_read()) {
            // if success, the object belongs to the caller thread as reader
            if (_read.compare_exchange_strong(i, i + 1))
                return ret;
            // Woops, reading out of order.
            ret->abort_read();
        }
        return NULL;
    }
    Element * get_for_write() {
        auto i = _write.load();
        Element * ret = &_elems[ i % _elems.size() ];
        if (ret->lock_for_write()) {
            // if success, the object belongs to the caller thread as writer
            if (_write.compare_exchange_strong(i, i + 1))
                return ret;
            // Woops, writing out of order.
            ret->abort_write();
        }
        return NULL;
    }
    void release_read(Element* e) { e->unlock_read();}
    void release_write(Element* e) { e->unlock_write();}
};