Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >单作者多读取器线程中的交换缓冲区

单作者多读取器线程中的交换缓冲区
EN

Stack Overflow用户
提问于 2011-10-18 02:41:12
回答 5查看 4.8K关注 0票数 5

故事

有一个写作线程,定期从某个地方收集数据(实时的,但这在问题中并不重要)。那时有许多读者从这些数据中阅读。通常的解决方案是使用两个读取器锁和两个缓冲区,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Writer (case 1):
acquire lock 0                        
loop
    write to current buffer
    acquire other lock
    free this lock
    swap buffers
    wait for next period

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Writer (case 2):
acquire lock 0                        
loop
    acquire other lock
    free this lock
    swap buffers
    write to current buffer
    wait for next period

问题

在这两种方法中,如果获取其他锁操作失败,则不执行任何交换,并且写入器将覆盖其先前的数据(因为写入器是实时的,它不能等待读取器),因此在这种情况下,所有读取器都会丢失该数据帧。

这并不是什么大问题,读者是我自己的代码,而且它们很短,所以使用双缓冲区,这个问题就解决了,如果有问题,我可以让它变成三重缓冲区(或更多)。

问题是我想尽量减少延误。想象一下案例1:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
writer writes to buffer0                reader is reading buffer1
writer can't acquire lock1              because reader is still reading buffer1
|                                       |
|                                       reader finishes reading,
| (writer waiting for next period)      <- **this point**
|
|
writer wakes up, and again writes to buffer0

在这一点上,理论上其他读者可以读取buffer0的数据,如果作者能够在阅读器完成后进行交换而不是等待下一个句点的话。在这种情况下发生的是,仅仅是因为一个读者有点晚,所有的读者错过了一帧数据,而问题完全可以避免。

案例2类似:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
writer writes to buffer0                reader is idle
|                                       |
|                                       reader finishes reading,
| (writer waiting for next period)
|
|                                       reader starts reading buffer1
writer wakes up                         |
it can't acquire lock1                  because reader is still reading buffer1
overwrites buffer0

我试着混合这些解决方案,所以作者尝试在写完之后立即交换缓冲区,如果不可能的话,在下一段时间醒来之后。所以就像这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Writer (case 3):
acquire lock 0                        
loop
    if last buffer swap failed
        acquire other lock
        free this lock
        swap buffers
    write to current buffer
    acquire other lock
    free this lock
    swap buffers
    wait for next period

现在拖延的问题仍然存在:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
writer writes to buffer0                reader is reading buffer1
writer can't acquire lock1              because reader is still reading buffer1
|                                       |
|                                       reader finishes reading,
| (writer waiting for next period)      <- **this point**
|
|
writer wakes up
swaps buffers
writes to buffer1

同样,在这一点上,所有的读者都可以开始阅读buffer0,这是在buffer0编写之后的一个短暂的延迟,但是他们不得不等到作者的下一段时间。

问题

问题是,我该怎么处理?如果我想让作者在期望的时间精确地执行,它需要使用RTAI函数等待这段时间,而我不能这样做

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Writer (case 4):
acquire lock 0                        
loop
    write to current buffer
    loop a few times or until the buffer has been swapped
        sleep a little
        acquire other lock
        free this lock
        swap buffers
    wait for next period

这会带来抖动。因为“几次”可能会比“等待下一段时间”更长,所以作者可能会错过这个时期的开始。

更清楚的是,我想要发生的事情是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
writer writes to buffer0                reader is reading buffer1
|                                       |
|                                       reader finishes reading,
| (writer waiting for next period)      As soon as all readers finish reading,
|                                         the buffer is swapped
|                                       readers start reading buffer0
writer wakes up                         |
writes to buffer1

我已经找到的

我找到了阅读-复制-更新,据我所知,它一直在为缓冲区分配内存,并将它们释放,直到读取器用完为止,这对我来说是不可能的,原因很多。第一,线程在内核和用户空间之间共享。第二,使用RTAI,您不能在实时线程中分配内存(因为这样您的线程就会调用Linux的系统调用,从而破坏实时性!(更别提使用Linux自己的RCU实现了,因为同样的原因)

我还考虑过有一个额外的线程,在更高的频率尝试交换缓冲区,但这听起来不是一个好主意。首先,它本身需要与作者同步,其次,我有许多这样的作者--读者在不同的部分并行工作,而每个作者需要额外的一个线程似乎太多了。对于所有作者来说,一个线程在与每个作者的同步方面似乎非常复杂。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-11-10 01:27:50

您用于读写器锁的API是什么?你有一个定时锁吗,比如时间锁?如果是,我认为这是您问题的解决方案,如下代码所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void *buf[2];

void
writer ()
{
  int lock = 0, next = 1;

  write_lock (lock);
  while (1)
    {
      abs_time tm = now() + period;

      fill (buf [lock]);
      if (timed_write_lock (next, tm))
        {
          unlock (lock);
          lock = next;
          next = (next + 1) & 1;
        }
      wait_period (tm);
    }
}


void
reader ()
{
  int lock = 0;
  while (1)
    {
      reade_lock (lock);
      process (buf [lock]);
      unlock (lock);
      lock = (lock + 1) & 1;
    }
}

这里发生的事情是,无论是等待锁还是等待下一段时间,对于作者来说都无关紧要,只要它一定会在下一阶段到来之前醒来。绝对超时确保了这一点。

票数 3
EN

Stack Overflow用户

发布于 2011-10-18 05:02:02

这不正是三重缓冲应该解决的问题吗。因此,您有3个缓冲区,让我们称它们为write1、write2和read。写入线程在写入write1和write2之间交替进行,确保它们从不阻塞,并且始终可用最后一个完整的框架。然后,在读线程中,在某个适当的点(例如,在读取帧之前或之后),用可用的写缓冲区翻转读缓冲区。

虽然这将确保写入程序不会阻塞(仅通过翻转两个指针就可以非常快地完成缓冲区翻转,甚至可以使用CAS原子而不是锁),但仍然存在读取器必须等待其他读取器在翻转之前完成读缓冲区的问题。我认为这可以稍微解决RCU-esque与一个池的读取缓冲器,其中一个可用的一个可以翻转。

票数 1
EN

Stack Overflow用户

发布于 2011-10-18 07:10:29

  • 使用队列(FIFO链接列表)
  • 实时写入器总是将(队列)追加到队列的末尾。
  • 读取器将始终从队列的开头删除(去队列)。
  • 如果队列为空,读取器将阻塞。

编辑以避免动态分配

我可能会用循环队列..。我将使用内置的__sync原子操作。http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html#Atomic-Builtins

  • 循环队列(FIFO 2d数组)
    • 字节数组=新的byteMAX_SIZE;

    • 开始和结束索引指针

  • 写入器覆盖ArrayEnd上的缓冲区
    • 作者可以增加开始,如果它结束循环所有的方式。

  • 读取器从ArrayStart获得缓冲区
    • 如果启动==结束,读取器将阻塞

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7805957

复制
相关文章
关于Android中工作者线程的思考
本文为 InfoQ 中文站特供稿件,首发地址为:http://www.infoq.com/cn/articles/android-worker-thread 如需转载,请与 InfoQ 中文站联系。
技术小黑屋
2018/09/05
7560
多线程中单例模式的优化
在编程中,单例模式是我们常用的一种设计模式,功能是保证在整个系统只用一个该对象的对象,具体代码如下:
付威
2020/01/21
7120
JAVASE中的多线程小结,多生产多消费案例.
因为线程任务已经被封装到Runnable接口中的run方法中,而这个run方法属于Runable接口的子类对象,所以要将这个子类对象作为参数传递给Thread类的构造方法,这样,线程对象创建时就可以明确要运行线程的任务。
帅的一麻皮
2020/02/21
7261
单例模式中的线程安全问题
所以我们在设计多线程代码的时候就必须在满足线程安全的前提下尽可能的提高任务执行的效 故: 加锁细粒度化:加锁的代码少一点,让其他代码可以并发并行的执行
终有救赎
2023/10/16
3100
单例模式中的线程安全问题
Go 语言并发编程系列(十一)—— sync 包系列:条件变量
简介 sync 包还提供了一个条件变量类型 sync.Cond,它可以和互斥锁或读写锁(以下统称互斥锁)组合使用,用来协调想要访问共享资源的线程。
学院君
2019/09/17
7550
Go 语言并发编程系列(十一)—— sync 包系列:条件变量
MFC 用户线程和工作者线程
用户线程 AfxBeginThread 创建线程 AfxEndThread 结束线程
ClearSeve
2022/02/10
3780
单工程多target的多App方案
早期在做新App时,由于不确定新App的发展路线,所以采用了拆分工程的方式,达成初期较快上线的目标,同时多App也不相互影响。 随着新产品迭代,越来越多需求要在多端上线,这部分需求都需要手动在多个Xcode工程同步,产生较大的成本。 本文就多App的方案做一些探讨。
落影
2023/05/27
4850
单工程多target的多App方案
Windows线程漫谈界面线程和工作者线程
每个系统都有线程,而线程的最重要的作用就是并行处理,提高软件的并发率。针对界面来说,还能提高界面的响应力。
全栈程序员站长
2022/07/15
6840
线程(四)线程池的实现+线程的单例模式
一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够 保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
海盗船长
2020/08/27
1.2K0
Oracle相邻表记录交换(单双两两交换)
看到了一个这样的提问:一个表中有id和name两个字段,id是连续非空不重复的,想要交换相邻表记录的name值(单双两两交换)。
星哥玩云
2022/08/18
2630
Oracle相邻表记录交换(单双两两交换)
线程安全的单例模式
【玩转 GPU】AI绘画、AI文本、AI翻译、GPU点亮AI想象空间-腾讯云开发者社区-腾讯云 (tencent.com)
疯狂的KK
2023/07/09
4520
线程安全的单例模式
Boost.Lockfree官方文档[通俗易懂]
术语“非阻塞”表示并发数据结构,该结构不使用传统的同步原语(例如警卫程序)来确保线程安全。 Maurice Herlihy和Nir Shavit(比较“多处理器编程的艺术”)区分了3种类型的非阻塞数据结构,每种结构具有不同的属性:
全栈程序员站长
2022/09/09
2.7K0
NET中解决KafKa多线程发送多主题的问题
  一般在KafKa消费程序中消费可以设置多个主题,那在同一程序中需要向KafKa发送不同主题的消息,如异常需要发到异常主题,正常的发送到正常的主题,这时候就需要实例化多个主题,然后逐个发送。   在
欢醉
2018/01/22
1.1K0
NET中解决KafKa多线程发送多主题的问题
C++多线程-单CPU下的多线程
多线程编程是现代软件技术中很重要的一个环节。要弄懂多线程,这就要牵涉到多进程?当然,要了解到多进程,就要涉及到操作系统。不过大家也不要紧张,听我慢慢道来。这其中的环节其实并不复杂。
cwl_java
2020/01/15
9650
c 线程安全的单例模式-详解C++实现线程安全的单例模式
  在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式。单例模式分为懒汉模式,跟饿汉模式两种。
宜轩
2022/12/29
9170
c 线程安全的单例模式-C++单例模式(线程安全、内存释放)
  一、懒汉模式:即第一次调用该类实例的时候才产生一个新的该类实例,并在以后仅返回此实例。
宜轩
2022/12/29
1.8K0
DS单链表--结点交换 C++
swap(int  pa, int pb)  //pa和pb表示两个结点在单链表的位置序号
叶茂林
2023/07/30
2930
交换知识 VLAN VTP STP 单臂路由
该文章介绍了技术社区中的内容编辑人员需要关注的几个重要方面。主要内容包括:1)技术社区中的内容编辑人员需要了解文章的背景和目的;2)技术社区中的内容编辑人员需要熟悉文章内容,并能够提出有效的建议;3)技术社区中的内容编辑人员需要具备一定的技术背景和专业知识,以便更好地理解文章内容;4)技术社区中的内容编辑人员需要具备良好的沟通和协调能力,以便更好地与作者和读者进行交流和互动。
惨绿少年
2017/12/27
1.5K0
清除 C/C++ 中的输入缓冲区
什么是缓冲区? 临时存储区域称为缓冲区。所有标准输入和输出设备都包含一个输入和输出缓冲区。在标准 C/C++ 中,流被缓冲,例如在标准输入的情况下,当我们按下键盘上的键时,它不会发送到您的程序,而是由操作系统缓冲,直到时间分配给那个程序。
鲸落c
2023/05/23
1.1K0
Java中四种线程安全的单例模式实现方式
第三种:懒汉模式改良版(线程安全,使用了double-check,即check-加锁-check,目的是为了减少同步的开销)
HaC
2020/12/30
2.8K0

相似问题

C# Dictionary<T,K>线程-单作者多读取器的线程安全

23

无锁多读取器单作者

40

Linux具有单作者、多读取器

11

多读取器,单写锁在Boost中

36

多线程单读取器单写fifo队列

43
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文