当前位置:  开发笔记 > 编程语言 > 正文

显示缓冲异步通道和通道之间差异的示例?

如何解决《显示缓冲异步通道和通道之间差异的示例?》经验,为你挑选了1个好方法。

从官方指南:

异步通道的"put"操作不会阻止 - 除非创建了具有缓冲区限制的给定通道并且已达到限制.

这是否意味着channel-put在其他线程使用时会被阻止channel-get,并且async-channel-put在其他线程仍在工作时async-channel-get

我想知道是否有任何例子可以显示它们的区别?



1> Alexis King..:
你本可以发明异步频道!

你的直觉是正确的:渠道往往会阻塞,但异步渠道通常不会.为什么?什么是确切的语义?好吧,首先,我们来谈谈普通频道.

通道

信道有效地是在球拍一个原始的,沿着螺纹作为跨线程通信的方式实现的.它们也是同步的,引用Racket参考:

频道是同步的; 发送方和接收方都必须阻塞,直到(原子)事务完成.多个发送者和接收者可以一次访问一个频道,但是为每个事务选择一个发送者和接收者.

这意味着通道是相当原始的特征 - 从通道读取和写入是单个动作,其中两个线程需要协调以便它们可以同时发送和接收.

使用隐喻,频道代表两个人之间的一些项目的转移,Alice和Bob.双方都同意会议地点.如果Alice先到达,她等待Bob到达那里,然后将项目交给Bob.如果Bob先到达,他会等待Alice给他这个项目.转移发生后,两人同时离开.

人是线程,项目是一些Racket值,会场是一个渠道.到达会场是从频道读取或写入,不得不等待是一个线程阻塞.离开是一个线程恢复.

考虑两个线程之间的简单交换:

#lang racket

(define channel (make-channel))

(define alice
  (thread (lambda ()
            (channel-put channel 'something)
            (displayln "Done!"))))

(define bob
  (thread (lambda ()
            (sleep 5)
            (let ([item (channel-get channel)])
              (sleep 5)
              (displayln item)))))

在上面的示例中,Done!即使Alice线程'something立即放入通道而不等待,也只会在五秒钟后打印.由于两个通道都需要协调,因此channel-put等待另一个线程(在本例中为Bob)进行调用,channel-get以便可以进行事务处理.

为什么我们需要阻止?

此时,您可能会问自己:为什么Alice需要等待?如果爱丽丝可以去会场,将物品放入垃圾箱,然后立即离开,那将会好得多.然后鲍勃可以在他到达时从垃圾箱中取出物品,爱丽丝可以继续她的生意.如果垃圾箱足够大,爱丽丝甚至可以在鲍勃拿出任何东西之前放入多个物品!

这是缓冲异步通道的想法.

缓冲异步通道

异步通道是Racket中一个简单的派生概念.它们可以使用内部可变缓冲区作为"bin"在通道之上实现.

异步通道由内部三部分组成:

    "输入"或"入队"通道,用于将值放入缓冲区的线程.

    "输出"或"出队"通道,用于从缓冲区中取值的线程.

    "缓冲区"或"队列",一个可变值,用于保存已放入异步通道但尚未取出的所有值.

通道显然只是Racket通道,但我们应该将什么用于缓冲区?好吧,Racket实际上在data/queue模块中提供了必要的队列实现,可以使用,但是Racket实现只是在可变对之上构建自己的队列.

为了管理这三个组件之间的交互,我们只需要一个协调读写一起的管理器线程.实现这一点非常简单,但我不会在这里重现它.如果您愿意,请查看async-channel.rkt在Racket中实现异步通道的模块.它有一些额外的好东西,我没有提到,但整个实现仍然少于300行.

让我们重新审视原始示例,但让我们使用异步通道而不是普通通道:

#lang racket

(require racket/async-channel)

(define channel (make-async-channel))

(define alice
  (thread (lambda ()
            (async-channel-put channel 'something)
            (displayln "Done!"))))

(define bob
  (thread (lambda ()
            (sleep 5)
            (let ([item (async-channel-get channel)])
              (sleep 5)
              (displayln item)))))

现在,Done!立即打印,因为puts不需要阻塞的线程.它只是将其粘贴在内部队列上,并且在获取值时无需关心.

注意事项

默认情况下,将值放入异步通道永远不会阻塞(您可以设置缓冲区大小限制,但这是可选的).但是,如果内部缓冲区为空,则从异步通道读取可以绝对阻止.根据我的经验,这通常是您想要的行为,但async-channel-try-get如果您需要,您可以随时检查值是否已准备就绪.

异步通道当然也是可变状态,所有关于突变的一般警告也适用于此.值得注意的是,单个通道不能有多个接收器,因为一旦执行了读操作,就会从队列中删除该值.如果要进行发布/子样式事件调度,请考虑使用多播异步通道包.

不过,除了陷阱之外,根据我的经验,异步频道几乎总是你想要的.频道是一个重要的原语,但它们使用起来很棘手.异步通道几乎只是工作,它们使多个线程之间的协作非常简单.只是要小心了解它们是如何工作的,这样你就不会用脚射击自己.

推荐阅读
围脖上的博博_771
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有