Can I use make(chan someStruct) in go?

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

Can I use make(chan someStruct) in go?

问题

例如:

type name struct {
    name string
    age int
}

func main() {
    c := make(chan name)

    c <- name{"sfsaf", 1}
    a, b := <- c

    close(c)
}

结果:

致命错误:所有的goroutine都处于休眠状态 - 死锁!

我想通过通道传递值。我应该怎么做?

英文:

for example:

type name struct {
    name string
    age int
}

func main() {
	  c := make(chan name)
	
	  c &lt;- name{&quot;sfsaf&quot;, 1}
	  a, b := &lt;- c
	  
	  close(c)
}

The result:
> fatal error: all goroutines are asleep - deadlock!

I want to pass values through channel. What should I do?

答案1

得分: 21

是的,你可以传递结构体。但这不是你的问题所在。

你在没有接收方准备好接收的情况下向通道发送了一个值。这就是导致死锁的原因。

通道期望“接收方”阻塞,等待“发送方”。这是通过使用 Goroutine 实现的。

因此,将发送方包装在一个 Goroutine 中,它不会立即执行。

package main

import (
	"fmt"
)

type name struct {
	name string
	age  int
}

func main() {
	c := make(chan name)

	go func() {
		c <- name{"sfsaf", 1}
		close(c)
	}()

	for n := range c {
		fmt.Println(n)
	}

	fmt.Println("channel was closed (all done!).")
}

在 playground 上查看:https://play.golang.org/p/uaSuCaB4Ms

这段代码之所以有效,是因为发送方的 Goroutine 还没有执行。直到当前正在执行的 Goroutine 被阻塞。

我们在 for n := range c 循环中被阻塞,等待接收值。(使用 for 循环迭代通道值是一种常见的模式,它会阻塞等待值的到来)。

因此,现在我们被阻塞在 for 循环中等待接收值,内联 Goroutine 将会执行,将我们的值发送到通道上。

此外,我们遵循安全的做法,在完成后清理并关闭通道,向 for 循环或 select 语句发出不再发送值的信号。发送方总是关闭通道,接收方不关闭。这是 for range 循环用于退出循环并继续执行其余代码的模式。


另外,你做得很好,传递了结构体的值而不是指针。

如果你传递了指针,你将不得不在对象周围实现一些互斥锁,以防止读写冲突。

不要通过共享内存来通信,而要通过通信来共享内存。

在通道和 Goroutine 之间传递值而不是指针,并获得好处。

英文:

Yes you can pass structs. But that's not the problem in your OP.

You sent a value on a channel, when there was no receiver ready to receive. That is what caused your deadlock.

Channels expect the receiver to be blocking, waiting for the sender. This is done with Goroutines.

Therefore, wrap your sender in a goroutine, which will not execute right away.

package main

import (
	&quot;fmt&quot;
)

type name struct {
	name string
	age  int
}

func main() {
	c := make(chan name)

	go func() {
		c &lt;- name{&quot;sfsaf&quot;, 1}
		close(c)
	}()

	for n := range c {
		fmt.Println(n)
	}

	fmt.Println(&quot;channel was closed (all done!).&quot;)
}

See it in the playground: https://play.golang.org/p/uaSuCaB4Ms

This works because the sender's goroutine is not executing yet. Not until the current goroutine executing gets blocked.

And we get blocked on the for n := range c loop. This is the receiver, sitting and waiting for values. (It is a common pattern to use the for loop to iterate over channel values as it will sit and block, waiting for values).

So now that we are blocked waiting to receive values in the for loop, the inline gorouting will now execute, to send our value on the channel.

In addition, we follow safe practices and tidy up after ourselves and close(c) the channel, signalling the for loop or select statement that there will be no more values sent. The sender always closes, never the receiver. This is the pattern the for range loop uses to exit the for loop, and continue executing the rest of your code.


As a side note, you did well by passing the value of the struct - not a pointer.

If you passed a pointer, you'd have to implement some mutex lock around the object to prevent a R/W panic.

Do not communicate by sharing memory; instead, share memory by communicating.

Stick to passing values, not pointers, around your channels and goroutines and reap the benefits.

huangapple
  • 本文由 发表于 2017年6月11日 13:00:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/44480524.html
匿名

发表评论

匿名网友

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

确定