gpt4 book ai didi

scheme - 显示缓冲异步 channel 和 channel 之间差异的示例?

转载 作者:行者123 更新时间:2023-12-04 16:59:10 26 4
gpt4 key购买 nike

official guide:

“put” operation of asynchronous channels does not block—unless the given channel was created with a buffer limit and the limit has been reached.



这是否意味着在其他使用 channel-put的线程中 channel-get会被阻塞,而在其他线程 async-channel-put的情况下 async-channel-get仍能工作?

我想知道是否有任何例子可以证明它们的不同?

最佳答案

您可能已经发明了异步 channel !

您的直觉是正确的: channel 往往会阻塞,但异步 channel 通常不会阻塞。为什么?确切的语义是什么?好吧,首先,让我们谈谈普通的 channel

channel 数

channel 实际上是Racket中的一个原语,与线程一起实现为跨线程通信的一种方式。它们也是同步的,引用the Racket reference:

Channels are synchronous; both the sender and the receiver must block until the (atomic) transaction is complete. Multiple senders and receivers can access a channel at once, but a single sender and receiver is selected for each transaction.



这意味着 channel 是相当原始的功能-从 channel 读取和写入是一个单一的 Action ,其中两个线程都需要协调以便它们可以同时发送和接收。

要使用隐喻, channel 代表了两个人(爱丽丝和鲍勃)之间某些物品的转移。双方都同意 session 地点。如果爱丽丝先到达,她将等鲍勃到达那里,然后将物品交给鲍勃。如果鲍勃先到,他会等爱丽丝给他物品。转移完成后,两个人同时离开。

人是线程,项目是一些 Racket 值,聚会场所是 channel 。到达 session 场所是从 channel 中读取或写入,而必须等待是线程阻塞。离开是线程恢复。

例子

考虑两个线程之间的简单交换:
#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)))))

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

为什么我们需要阻止?

此时,您可能会问自己:为什么爱丽丝需要等待?如果爱丽丝可以去聚会地点,将物品放进垃圾箱,然后立即离开,那就好多了。这样,鲍勃到达时就可以从箱子里拿出东西,而爱丽丝则可以继续她的生意。如果垃圾箱足够大,在鲍勃拿出任何东西之前,爱丽丝甚至可以将多个物品放进其中!

这是 缓冲的异步 channel 的想法。

缓冲异步 channel

异步 channel 是Racket中一个简单的派生概念。可以使用内部可变缓冲区作为“容器”在 channel 顶部实现它们。

异步 channel 在内部由三部分组成:
  • “输入”或“入队” channel ,用于将值放入缓冲区的线程。
  • “输出”或“出队” channel ,用于将值从缓冲区中取出的线程。
  • “缓冲区”或“队列”,一个可变的值,包含已放入异步 channel 但尚未取出的所有值。

  • 这些 channel 显然只是Racket channel ,但是我们应该对缓冲区使用什么呢?好吧,Racket实际上提供了可以使用的 an imperative queue implementation in the data/queue module,但是Racket实现只是在 mutable pairs之上构建了自己的队列。

    要管理这三个组件之间的交互,我们只需要一个管理器线程即可协调读写操作。事实证明,这种实现是相当琐碎的,但是在此我将不再赘述。如果您愿意,请看一下 async-channel.rkt ,它是在Racket中实现异步 channel 的模块。它有很多我没有提到的优点,但是整个实现仍然少于300行。

    例子

    让我们重新看一下原始示例,但让我们使用异步 channel 而不是普通 channel :
    #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!立即打印,因为 put的线程不需要阻塞。它只是将其粘贴在内部队列上,无需关心何时获取值。

    注意事项

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

    异步 channel 当然也是可变的,有关突变的所有一般警告也都适用于此。值得注意的是,单个 channel 不能有多个接收器,因为一旦执行了读取操作,该值就会从队列中删除。如果要发布/订阅样式事件,请考虑使用 Multicast Asynchronous Channels包。

    尽管如此,根据我的经验,异步 channel 几乎总是您想要的。 channel 是一个重要的原始变量,但使用起来很棘手。异步 channel 几乎可以正常工作,并且使多个线程之间的协作非常简单。请小心了解它们的工作原理,以免造成 footer 受伤。

    关于scheme - 显示缓冲异步 channel 和 channel 之间差异的示例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34247983/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com