确保 Go 通道不会阻塞的可靠方法

huangapple go评论89阅读模式
英文:

Reliable way to ensure a Go channel does not block

问题

我正在寻找一种可靠的方法,确保在Go中的空通道不会阻塞我的执行。我必须按特定顺序(一种优先级)迭代通过一些通道,并且一旦找到其中一个通道中有项目,就读取一个项目。

目前,我以类似的方式处理:

if len(myChannel) > 0 {
    // 可能存在问题:长度可能已经变为0,使得这个操作变为阻塞
    elm := <- myChannel
    return elm
}

理论上,这可能导致等待时间过长,而另一个通道可能有一个准备好的项目等待"服务"。

有什么改进的建议吗?我可以在通道中使用互斥锁,但感觉还有更好的解决方案,尽管我不确定是什么。

英文:

I'm looking for a reliable to way to make sure an empty channel in Go does not block my execution. I have to iterate through a number of channels in a particular order (kind of priorities), and once I find one with items in it, read one.

Currently I do something in a similar way:

if len(myChannel) &gt; 0 {
    // Possible issue here: length could have changed to 0 making this blocking
    elm := &lt;- myChannel
    return elm
}

In theory this could result into too-long of waiting, while a different channel might have an item which is ready to be "served".

Any suggestions on how to improve? I could use a mutex in the channel, but it feels like there's a better solution although I'm not sure how.

答案1

得分: 2

有一个reflect.Select函数可能可以满足你的需求:

package main

import (
	"fmt"
	"reflect"
	"time"
)

func main() {
	a, b, c := make(chan int), make(chan int), make(chan int)
	go func() {
		time.Sleep(2 * time.Second)
		a <- 1
	}()
	go func() {
		time.Sleep(time.Second)
		b <- 2
	}()
	go func() {
		time.Sleep(3 * time.Second)
		c <- 3
	}()
	for i := 0; i < 3; i++ {
		chosen, recv, ok := reflect.Select([]reflect.SelectCase{
			reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(a),
			},
			reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(b),
			},
			reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(c),
			},
		})
		if ok {
			fmt.Printf("从 %d 接收到值 %d\n", chosen, recv.Interface().(int))
		}
	}
}

play.golang.org

英文:

There is a reflect.Select function that might do what you want:

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
	&quot;time&quot;
)

func main() {
	a, b, c := make(chan int), make(chan int), make(chan int)
	go func() {
		time.Sleep(2 * time.Second)
		a &lt;- 1
	}()
	go func() {
		time.Sleep(time.Second)
		b &lt;- 2
	}()
	go func() {
		time.Sleep(3 * time.Second)
		c &lt;- 3
	}()
	for i := 0; i &lt; 3; i++ {
		chosen, recv, ok := reflect.Select([]reflect.SelectCase{
			reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(a),
			},
			reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(b),
			},
			reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(c),
			},
		})
		if ok {
			fmt.Printf(&quot;Got value %d from %d\n&quot;, recv.Interface().(int), chosen)
		}
	}
}

play.golang.org

答案2

得分: -1

我不确定这是否真正回答了问题“有没有一种可靠的方法来确保 Go 通道不会阻塞”。在提问者的使用情况中,如果接收操作阻塞了,只要集合中的其他通道不会阻塞,那就没问题,而这也是被接受的解决方案所实现的。这与“确保接收操作不会阻塞”是不同的,我看不到绕过提问者指出的竞态条件基本限制的方法。

英文:

I'm not sure this really answers the question "Is there a reliable way to ensure a Go channel does not block". In the OP's use case, it's OK if a recv blocks /so long as no other channel in the set would not block/, and that's what the accepted solution implements. This is different from "ensure the recv does not block", and I see no way around the fundamental limitation of the race condition OP points out.

huangapple
  • 本文由 发表于 2014年12月29日 18:28:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/27687114.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定