简体   繁体   English

同时选择一个发送和接收频道

[英]Select on a go send and receive channel at the same time

Suppose I have a buffered send and unbuffered receive channel: 假设我有一个缓冲的发送和无缓冲接收通道:

s := make(chan<- int, 5)
r := make(<-chan int)

Is it possible to select on them both, so that r will be selected if it has anything to read, and s will be selected if it is not full? 是否有可能同时select它们,以便在有任何内容可以读取时选择r ,如果它没有满,则会选择s Something equivalent to this, but not using 100% CPU: 相当于此的东西,但不使用100%CPU:

for {
    if len(s) < cap(s) {
        // Send something
    }
    if len(r) > 0 {
        // Receive something
    }
}

Note that I want to decide what to send at the time that I send it, not earlier. 请注意,我想决定在发送时发送的内容,而不是更早。

Edit 编辑

This question is basically equivalent to "Can I block until a channel is ready-to-send, without sending anything?" 这个问题基本上等同于“我可以阻止直到某个频道准备发送, 而不发送任何内容吗?”

You can do this with select but since the value to be sent is evaluated only once, if both channel are not ready, the value to be sent would become outdated by the time it can be sent. 您可以使用select执行此操作,但由于要发送的值仅评估一次,如果两个通道都未就绪,则要发送的值将在发送时变得过时。

So add a default case which will be executed if none of the channels are ready, in which you just "sleep" a little, then try again (with an updated new value calculated/acquired to be sent). 因此,添加一个default情况,如果没有任何通道准备就会执行,你只需要“睡眠”一点,然后再试一次(计算/获取更新后的新值)。 By sleeping you will not consume CPU resources: 通过休眠,您将不会消耗CPU资源:

s := make(chan<- int, 5)
r := make(<-chan int)

for {
    v := valueToSend() // Evaluated each time we try to send
    select {
    case s <- v:
        fmt.Println("Sent value:", v)
    case vr := <-r:
        fmt.Println("Received:", vr)
    default: // If none are ready currently, we end up here
        time.Sleep(time.Millisecond * 1)
    }
}

Note that checking the length or capacity of a channel and then sending/receiving is not considered a good solution because the channel might become not ready between the time you check its length/cap and you try to send/receive, as illustrated below: 请注意 ,检查频道的长度或容量然后发送/接收不被视为一个好的解决方案,因为在您检查其长度/上限和尝试发送/接收之间,频道可能没有准备好,如下所示:

if len(r) > 0 {
    // r is ready to receive

    // Optional other code here,
    // meanwhile another goroutine might receive the value from r!

    r <-  // If other goroutine received from r, this will block!
}

It's a simple select: 这是一个简单的选择:

select {
case s <- n:
    // Successful send.
case n := <- r:
    // Successful receive. Do something with n.
}

Can I block until a channel is ready-to-send, without sending anything? 我可以阻止直到某个频道准备发送,而不发送任何内容吗?

Not with primitive go channels. 不是原始的go频道。 You could probably manage to pull something together using the SharedBuffer type in my channels library, but even that is complicated and it uses a great deal of reflection under the covers. 您可能可以设法使用我的频道库中的SharedBuffer类型将某些东西拉到一起,但即使这样也很复杂,它在封面下使用了大量的反射。

https://godoc.org/github.com/eapache/channels#SharedBuffer https://godoc.org/github.com/eapache/channels#SharedBuffer

Instead of sending the value directly, you could send an object which can compute the value. 您可以发送一个可以计算值的对象,而不是直接发送值。 Then you can detect when the object is sent, and then compute. 然后,您可以检测对象何时发送,然后进行计算。 You can use sync.Once to make sure the computation is done once, and gate access to the result. 您可以使用sync.Once确保计算完成一次,并对结果进行门控访问。 This avoids using Sleeps. 这可以避免使用Sleeps。

Something like this: https://play.golang.org/p/oL2HA2jl91 像这样: https//play.golang.org/p/oL2HA2jl91

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM