C++ 迭代器,调用end()的结果的值会随着数据添加到循环缓冲区而改变

C++ 迭代器,调用end()的结果的值会随着数据添加到循环缓冲区而改变,c++,boost,iterator,std,circular-buffer,C++,Boost,Iterator,Std,Circular Buffer,今天早些时候我发现,boost::circular缓冲区的迭代器在多线程环境中的行为与我预期的不一样。(虽然公平地说,它们的行为也不同于我在单线程程序中所想的) 如果调用buffer.begin()和buffer.end()来表示用于循环特定数据序列的迭代器。如果将更多数据添加到循环\u缓冲区,则结束迭代器的值将更改。显然,如果在数据更改后再次调用end(),您会期望得到不同的结果。但令人困惑的是您已经更改的迭代器对象的值 是否有一种方法可以创建迭代器,使您能够处理循环缓冲区中的一组数据,并且即

今天早些时候我发现,
boost::circular
缓冲区的迭代器在多线程环境中的行为与我预期的不一样。(虽然公平地说,它们的行为也不同于我在单线程程序中所想的)

如果调用
buffer.begin()
buffer.end()
来表示用于循环特定数据序列的迭代器。如果将更多数据添加到
循环\u缓冲区
,则结束迭代器的值将更改。显然,如果在数据更改后再次调用
end()
,您会期望得到不同的结果。但令人困惑的是您已经更改的迭代器对象的值

是否有一种方法可以创建迭代器,使您能够处理循环缓冲区中的一组数据,并且即使在处理一组数据时向缓冲区添加了其他数据,迭代器的值也不会更改

如果没有,是否有一个首选的“非迭代器”模式可以应用,或者有一个不同的容器类可以允许这样做

#include "stdafx.h"
#include <boost\thread.hpp>
#include <boost\thread\thread_time.hpp>
#include <boost\circular_buffer.hpp>

int _tmain(int argc, _TCHAR* argv[])
{
    boost::circular_buffer<int> buffer(20);

    buffer.push_back(99);
    buffer.push_back(99);
    buffer.push_back(99);

    boost::circular_buffer<int>::const_iterator itBegin = buffer.begin();
    boost::circular_buffer<int>::const_iterator itEnd = buffer.end();

    int count = itEnd - itBegin;
    printf("itEnd - itBegin == %i\n", count); //prints 3

    /* another thread (or this one) pushes an item*/
    buffer.push_back(99);

    /*we check values of begin and end again, they've changed, even though we have not done anything in this thread*/
    count = itEnd - itBegin;
    printf("itEnd - itBegin == %i\n", count); //prints 4 
}
#包括“stdafx.h”
#包括
#包括
#包括
int _tmain(int argc,_TCHAR*argv[]
{
boost::循环缓冲区(20);
缓冲器。推回(99);
缓冲器。推回(99);
缓冲器。推回(99);
boost::circular\u buffer::const\u迭代器itBegin=buffer.begin();
boost::circular\u buffer::const\u迭代器itEnd=buffer.end();
int count=itEnd-itBegin;
printf(“itEnd-itBegin=%i\n”,count);//打印3
/*另一个线程(或此线程)推送一个项目*/
缓冲器。推回(99);
/*我们再次检查begin和end的值,它们已经更改,即使我们在这个线程中没有做任何事情*/
计数=itEnd-itBegin;
printf(“itEnd-itBegin=%i\n”,count);//打印4
}

更新以获取对ronag的更详细响应 我正在寻找生产者-消费者类型的模型,boost文档中显示的有界缓冲区示例与我需要的非常接近。除了以下两个例外

  • 我需要能够一次从缓冲区中读取多个数据元素,检查它们(这可能不是小事),然后选择要从缓冲区中删除多少项

    假例子:试图从一个字符串中处理两个单词hello&world

    读取1,缓冲区包含h-e-l,不要从缓冲区中删除字符。 -多产 读取2,缓冲区包含h-e-l-l-o-w-o,我发现“hello”删除了5个字符,缓冲区现在有w-o -多产 读取3,缓冲区包含w-o-r-l-d,处理“世界”,删除另外5个字符

  • 我不需要在读取时阻塞生产者线程,除非缓冲区已满


  • 文档明确声明循环缓冲区和用户负责锁定数据结构。那会解决你的问题

    但是生产者-消费者风格的队列可能更适合您的问题。

    根据,迭代器应该保持有效。(在同时对容器进行变异和迭代时,我总是首先检查。)因此,您的示例代码是合法的

    发生的情况是由于STL迭代器约定:
    end()
    不指向某个元素,而是指向最后一个元素之后的假想元素。第四次
    后推
    ,从
    itBegin
    (第一个元素)到
    itEnd
    (超过最后一个元素)的距离增加

    一种解决方案可能是保持一个指向具体元素的迭代器,例如

    itPenultimate = itEnd - 1;
    

    现在,从
    itBegin
    itpenutimate
    的距离将保持不变,即使循环缓冲区被扩展,只要它不在该范围内。

    有可能
    push_back
    使迭代器无效,从而使迭代器以UB结束。在给出的示例代码中,
    push_back
    不会使任何迭代器无效。我(和OP)不明白为什么在容器上调用
    push_back
    会导致局部变量
    itEnd
    发生更改。@MooingDuck
    push_back
    itEnd
    之前插入了一个元素,从而导致从
    itBegin
    itEnd
    的距离增加。如果在任意两个迭代器之间插入缓冲区,也会发生同样的情况。@MooingDuck
    count
    不会改变,而且
    itEnd
    也不会改变。改变的是
    itEnd-itBegin
    。这就像一个链子的两端,有人在中间插入一个链接。你是对的,坏例子。一个更好的澄清者是:为什么它的行为不像
    vector
    /
    deque
    /
    数组
    ?相反,它的行为类似于基于节点的容器
    boost::circular_buffer
    看起来它应该像一个
    向量
    /
    deque
    /
    数组
    。很好的解释,我最终使用boost::iterator_facade实现了一个自定义迭代器,它提供了我想要的行为。