代码之家  ›  专栏  ›  技术社区  ›  hplbsh

实时查看循环缓冲区中的数据

  •  1
  • hplbsh  · 技术社区  · 16 年前

    我有一个传入的消息流,并希望有一个窗口,允许用户滚动通过消息。

    • 传入消息进入单生产者单消费者队列
    • 线程读取它们并将它们放入具有顺序id的循环缓冲区中
    • 通过这种方式,我可以将多个传入流安全地放置在循环缓冲区中,并将输入解耦
    • 互斥以协调UI和线程之间的循环缓冲区访问
    • 这使UI能够确定它可以显示什么,需要访问循环缓冲区的哪些部分,删除覆盖的消息。它只访问以当前大小和滚动位置填充窗口所需的消息。

    这听起来像是正确的方法吗?有什么可以让它更可口的地方吗?

    2 回复  |  直到 16 年前
        1
  •  4
  •   Test    16 年前

    (请参见下面的Effo编辑,此部件已弃用)

    当消息到达时,线程弹出消息并相应地将其推送到UI队列中。

    此外,每个UI.Q也可以原子化操作。不需要互斥。另一个好处是,每条消息只复制了两次:一个是到低级队列,另一个是到显示器,因为不需要将消息存储到其他位置(如果是C/C++,只需将一个指针从低级队列分配到UI.Q就足够了)。

    我更喜欢动态大小队列的建议。似乎现代个人电脑上有很多内存。

    请参阅位于的文件EffoNetMsg.pdf http://code.google.com/p/effonetmsg/downloads/list


    埃福EDIT@2009oct23:显示支持随机消息访问以滚动消息查看器的分段模型。

                             +---------------+ 
                         +---> Ring Buffer-1 <---+
                         |   +---------------+   |
                      +--+                       +-----+
                      |  |   +---------------+   |     |
                      |  +---> Ring Buffer-2 <---+     |
                      |      +---------------+         |
                      |                                |
              +-------+-------+            +-----------+----------+
              |   Push Msg &  |            |   GetHeadTail()      |
              |  Send AckReq  |            |  & Send UpdateReq    |
              +---------------+            +----------------------+
              |App.MsgStage() |            |   App.DisPlayStage() |
              +-------+-------+            +-----------+----------+
                      | Pop()                          | Pop()         
     ^              +-V-+                            +-V-+ 
     | Events       | Q |    Msg Stage |             | Q |  Display Stage
     | Go Up        | 0 |   Logic-Half |             | 1 |   Logic-Half      
    -+------------- |   | -------------+------------ |   | ---------------
     | Requests     |   |    I/O-Half  |             |   |    I/O-Half
     | Move Down    +-^-+              |             +-^-+   
     V                | Push()                         |     
       +--------------+-------------+                  |
       |   Push OnRecv Event,       |          +-------+-------+
       | 1 Event per message        |          |               | Push()
       |                            |   +------+------+ +------+------+
       |  Epoll I/O thread for      |   |Push OnTimer | |Push OnTimer |
       |multi-messaging connections |   |  Event/UI-1 | |  Event/UI-2 |
       +------^-------^--------^----+   +------+------+ +------+------+
              |       |        |               |               |                   
    Incoming msg1    msg2     msg3        Msg Viewer-1    Msg Viewer-2              
    

    要点:

    1您了解不同的高度并发模型,具体如上图所示,分阶段模型;这样你就能知道它为什么跑得快。

    2两种I/O,如果是C/C++和GNU Linux 2.6x,一种是消息传递或Epoll线程;另一种是显示,如绘图屏幕或打印文本等。这两种I/O被相应地处理为两个阶段。注意:如果是Win/MSVC,请使用完成端口而不是Epoll。

    如前所述,仍有2条消息被复制。a) Push-OnRecv生成消息(“CMsg*pMsg=CreateMsg(msg)”如果是C/C++);b) UI相应地从其环形缓冲区读取和复制消息,只需要复制更新的消息部分,而不需要复制整个缓冲区。注意:队列和环形缓冲区仅存储一个消息句柄(“queue.push(pMsg)”或“RingBuff.push(pMsg)”(如果是C/C++),任何过期的消息都将被删除(“pMsg->Destroy()”(如果是C/C++)。通常,MsgStage()会在将消息头推入环形缓冲区之前重新生成消息头。

    5确切地说,OnTimer或OnRecv是事件名称,OnTimer(){}或OnRecv(){}将在DisplayStage()或MsgStage()中执行。同样,事件向上,请求向下,这可能与您之前的想法或看到的有所不同。

    6个Q0和2个环形缓冲器可以实现为无锁设施,以提高性能,因为单个生产者和单个消费者;不需要锁/互斥锁。而Q1则有所不同。但我相信,通过稍微更改上述设计图,您也可以实现单一生产者和单一消费者,例如添加Q2,使每个UI都有一个队列,DisplayStage()只需轮询Q1和Q2即可正确处理所有事件。注:Q0和Q1是事件队列,上图中未显示请求队列。

    7 MsgStage()和DisplayStage()顺序位于单个stageModel.Stage()中,比如说主线程。Epoll I/O或消息传递是另一个线程,MsgIO线程,每个UI都有一个I/O线程,比如显示线程。在上图中,总共有4个线程同时运行。Effo已经测试过,对于多个Lisener加上数千个消息传递客户端来说,仅仅一个MsgIO线程就足够了。

    http://code.google.com/p/effonetmsg/downloads/list 或effoadons.pdf,网址为 http://code.google.com/p/effoaddon/downloads/list 了解更多关于高度并发编程模型和网络消息传递的信息;请参阅EffoDesign_LockFree.pdf,网址为 http://code.google.com/p/effocore/downloads/list 了解更多关于无锁设施的信息,如无锁队列和无锁环形缓冲区。

        2
  •  2
  •   ChrisW    16 年前

    向GUI发出的通知不应包含ID,即当前值。相反,它应该只说“当前值已更改”,然后让GUI读取该值:因为在发送通知和GUI读取该值之间可能存在延迟,并且您希望GUI读取当前值(而不是潜在的过时值)。您希望它是一个异步通知。

    此外,您还可以限制通知,例如每秒发送不超过5或20次(如有必要,可将通知延迟50至200毫秒)。