2016-11-12 18 views
1

我有一个结构叫做HubRun()方法,它在自己的goroutine中执行。该方法按顺序处理传入消息。消息从多个生产者同时到达(单独的goroutines)。当然,我使用channel来完成这项任务。但是现在我想将Hub隐藏在interface之后,以便能够从其实现中进行选择。所以,使用channel作为简单的Hub的字段是不合适的。是否可以隐藏发送到函数调用后的通道

package main 
import "fmt" 
import "time" 

type Hub struct { 
    msgs chan string 
} 
func (h *Hub) Run() { 
    for { 
     msg, hasMore := <- h.msgs 
     if !hasMore { 
      return 
     } 
     fmt.Println("hub: msg received", msg) 
    } 
} 
func (h *Hub) SendMsg(msg string) { 
    h.msgs <- msg 
} 

func send(h *Hub, prefix string) { 
    for i := 0; i < 5; i++ { 
     fmt.Println("main: sending msg") 
     h.SendMsg(fmt.Sprintf("%s %d", prefix, i)) 
    } 
} 

func main() { 
    h := &Hub{make(chan string)} 
    go h.Run() 
    for i := 0; i < 10; i++ { 
     go send(h, fmt.Sprintf("msg sender #%d", i)) 
    } 
    time.Sleep(time.Second) 
} 

所以我介绍了Hub.SendMsg(msg string)功能只是调用h.msgs <- msg和我可以添加到HubInterface。作为一个Go-我想知道,从并发的角度来看是否安全?如果是这样 - 在Go这是一种常用的方法吗?

游乐场here

+3

是的。我不确定还有别的答案,因为我不确定什么会导致你相信它可能是不安全的。 – JimB

+0

@JimB,实际上,在思考了一段时间后它是如何工作的,我意识到答案是非常明显的,只要写入通道是线程安全的。 –

+1

_单通道可用于发送语句,接收操作以及通过任意数量的goroutine调用内置函数cap和len,而无需进一步同步。_另请参阅[如果我正确使用通道,则需要使用互斥?](http://stackoverflow.com/questions/34039229/if-i-am-using-channels-properly-should-i-need-to-use-mutexes) – icza

回答

2

将发送到方法中时,通道发送语义不会改变。 Andrew的回答指出,需要使用make创建频道才能成功发送频道,但无论发送是否在方法内,情况总是如此。

如果您担心确保呼叫者可以不小心与一nil通道无效Hub情况下拉闸,一种方法是使结构类型私有(hub),并有一个NewHub()函数返回一个完全初始化hub包裹在你的界面类型中。由于结构是私有的,所以其他包中的代码不能用不完整的结构字面值(或任何结构字面值)初始化它。

这就是说,它是经常可以创建在围棋无效或无义值而这接受:net.IP("HELLO THERE BOB")是有效的语法,或net.IP{}。所以如果你认为最好把你的Hub类型放在前面。

-1

简单的答案

更好的答案

没有

渠道是伟大的未知旅途例程发射数据。他们这样做是安全的,但我会建议小心几个部分。在列出的例子中,通道是由消费者(而不是由消费者)构造结构体创建的。

说消费者创建如下的Hub:&Hub{}。完全有效......除了所有SendMsg()的调用都会永久阻止的事实。幸运的是,你把这些放在他们自己的例程中。所以你还好吧?错误。你现在正在泄露例行公事。似乎很好......直到你运行一段时间。 Go鼓励你拥有有效的零值。在这种情况下,&Hub{}无效。

确保SendMsg()不会阻止,可以通过select{}来实现,但是当您遇到默认情况时(例如丢弃数据),您必须决定如何处理。该频道可能会阻止更多的原因,而不是坏的设置。稍后再说,您不仅仅是在从频道中读取数据后才打印数据。如果读取速度非常慢,或者在IO上阻塞会怎么样。然后你会开始推回生产者。

最终,通道可以让您不必考虑太多的并发性......但是,如果这是高吞吐量,那么您有相当多的考虑。如果是生产代码,那么您需要了解您的API涉及SendMsg()阻止。

+2

这是积极混淆;问的问题是通道发送语义是否通过将发送移入一个函数而改变,而他们不是。无论通道发送是否在函数调用中,'&Hub {}'都是类型无限制的无效值。 – twotwotwo

相关问题