显示缓冲异步通道和通道之间区别的示例?
Examples to show the difference between Buffered Asynchronous Channels and Channels?
“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
?
我想知道是否有任何例子可以说明它们的区别?
你本可以发明异步通道!
您的直觉是正确的:通道往往会阻塞,但异步通道通常不会。为什么?确切的语义是什么?嗯,先来说说普通的channels.
频道
A channel 实际上是 Racket 中的原语,作为 cross-thread 通信的一种方式与线程一起实现。它们也是同步,引用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.
这意味着通道是相当原始的功能——从通道读取和写入是一个单一的操作,其中两个线程需要协调以便它们可以同时发送和接收。
打个比方,渠道代表两个人(爱丽丝和鲍勃)之间的某些物品的转移。双方就会议地点达成一致。如果爱丽丝先到,她会等鲍勃到达那里,然后把物品交给鲍勃。如果 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
以便交易发生。
为什么我们需要屏蔽?
此时,您可能会问自己:为什么爱丽丝需要等待?如果爱丽丝可以去会面地点,将物品丢进垃圾箱,然后立即离开,那就更好了。然后 Bob 可以在他到达时从垃圾箱中挑选物品,而 Alice 可以继续她的业务。如果垃圾箱足够大,Alice 甚至可以在 Bob 拿出任何东西之前将多个物品放入其中!
这是缓冲异步通道的想法。
缓冲异步通道
异步通道是 Racket 中一个简单的派生概念。它们可以使用内部可变缓冲区作为“bin”在通道之上实现。
异步通道由三部分组成,内部:
- “输入”或“入队”通道,用于将值放入缓冲区的线程。
- “输出”或“出队”通道,供线程从缓冲区中取出值。
- “缓冲区”或“队列”,一个可变值,包含所有已放入异步通道但尚未取出的值。
通道显然只是Racket通道,但是我们应该用什么来缓冲呢?嗯,Racket其实提供了an imperative queue implementation in the data/queue
module, which could be used, but the Racket implementation just builds its own queue on top of mutable pairs.
要管理这三个组件之间的交互,我们只需要一个协调读写的管理器线程即可。事实证明这个实现相当简单,但我不会在这里重现。如果你愿意,可以看看 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!
立即打印,因为 put
s 的线程不需要阻塞。它只是将它放在内部队列中,不需要关心何时获取值。
注意事项
默认情况下,将值放入异步通道永远不会阻塞(您可以设置缓冲区大小限制,但这是可选的)。但是,如果内部缓冲区为空,则从异步通道读取 绝对会阻塞。根据我的经验,这通常是您想要的行为,但如果需要,您始终可以使用 async-channel-try-get
检查值是否准备就绪。
当然,异步通道也是可变状态,所有关于突变的一般警告也适用于此。值得注意的是,单个通道不能有多个接收器,因为一旦执行了读取操作,该值就会从队列中删除。如果你想要 pub/sub 风格的事件调度,考虑使用 Multicast Asynchronous Channels 包。
不过,撇开陷阱不谈,根据我的经验,异步通道几乎总是您想要的。通道是一个重要的原语,但使用起来却很棘手。异步通道几乎可以正常工作,它们使多个线程之间的协作变得非常简单。请仔细了解它们的工作原理,以免搬起石头砸自己的脚。
“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
?
我想知道是否有任何例子可以说明它们的区别?
你本可以发明异步通道!
您的直觉是正确的:通道往往会阻塞,但异步通道通常不会。为什么?确切的语义是什么?嗯,先来说说普通的channels.
频道
A channel 实际上是 Racket 中的原语,作为 cross-thread 通信的一种方式与线程一起实现。它们也是同步,引用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.
这意味着通道是相当原始的功能——从通道读取和写入是一个单一的操作,其中两个线程需要协调以便它们可以同时发送和接收。
打个比方,渠道代表两个人(爱丽丝和鲍勃)之间的某些物品的转移。双方就会议地点达成一致。如果爱丽丝先到,她会等鲍勃到达那里,然后把物品交给鲍勃。如果 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
以便交易发生。
为什么我们需要屏蔽?
此时,您可能会问自己:为什么爱丽丝需要等待?如果爱丽丝可以去会面地点,将物品丢进垃圾箱,然后立即离开,那就更好了。然后 Bob 可以在他到达时从垃圾箱中挑选物品,而 Alice 可以继续她的业务。如果垃圾箱足够大,Alice 甚至可以在 Bob 拿出任何东西之前将多个物品放入其中!
这是缓冲异步通道的想法。
缓冲异步通道
异步通道是 Racket 中一个简单的派生概念。它们可以使用内部可变缓冲区作为“bin”在通道之上实现。
异步通道由三部分组成,内部:
- “输入”或“入队”通道,用于将值放入缓冲区的线程。
- “输出”或“出队”通道,供线程从缓冲区中取出值。
- “缓冲区”或“队列”,一个可变值,包含所有已放入异步通道但尚未取出的值。
通道显然只是Racket通道,但是我们应该用什么来缓冲呢?嗯,Racket其实提供了an imperative queue implementation in the data/queue
module, which could be used, but the Racket implementation just builds its own queue on top of mutable pairs.
要管理这三个组件之间的交互,我们只需要一个协调读写的管理器线程即可。事实证明这个实现相当简单,但我不会在这里重现。如果你愿意,可以看看 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!
立即打印,因为 put
s 的线程不需要阻塞。它只是将它放在内部队列中,不需要关心何时获取值。
注意事项
默认情况下,将值放入异步通道永远不会阻塞(您可以设置缓冲区大小限制,但这是可选的)。但是,如果内部缓冲区为空,则从异步通道读取 绝对会阻塞。根据我的经验,这通常是您想要的行为,但如果需要,您始终可以使用 async-channel-try-get
检查值是否准备就绪。
当然,异步通道也是可变状态,所有关于突变的一般警告也适用于此。值得注意的是,单个通道不能有多个接收器,因为一旦执行了读取操作,该值就会从队列中删除。如果你想要 pub/sub 风格的事件调度,考虑使用 Multicast Asynchronous Channels 包。
不过,撇开陷阱不谈,根据我的经验,异步通道几乎总是您想要的。通道是一个重要的原语,但使用起来却很棘手。异步通道几乎可以正常工作,它们使多个线程之间的协作变得非常简单。请仔细了解它们的工作原理,以免搬起石头砸自己的脚。