web-dev-qa-db-ja.com

make(chan someStruct)をgoで使用できますか?

例えば:

type name struct {
    name string
    age int
}

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

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

      close(c)
}

結果:

致命的なエラー:すべてのゴルーチンが眠っています-デッドロック!

チャネルを通じて値を渡したいのですが。私は何をすべきか?

7
GZ Xue

はい、構造体を渡すことができます。しかし、それはあなたのOPの問題ではありません。

受信する準備ができているレシーバーがなかったときに、チャネルで値を送信しました。それがデッドロックの原因です。

チャンネルはreceiversenderを待ってブロックしていることを期待しています。これはGoroutinesで行われます。

したがって、すぐには実行されない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!).")
}
_

遊び場でそれを見てください: https://play.golang.org/p/uaSuCaB4Ms

これは、送信者のゴルーチンがまだ実行されていないため機能します。現在実行中のゴルーチンがブロックされるまでは。

そして、_for n := range c_ループでブロックされます。これはレシーバーであり、座って値を待っています。 (値を待って座ってブロックするため、forループを使用してチャネル値を反復するのは一般的なパターンです)。

これで、forループで値を受信するための待機がブロックされたため、インラインゴールーティングが実行され、チャネルに値を送信します。

さらに、安全な慣行に従い、自分自身とチャネルのclose(c)を整理して、forループまたはselectステートメントに、送信される値がないことを通知します。 送信側は常にクローズし、受信側はクローズしません。これは、for rangeループがforループを終了して残りのコードの実行を続けるために使用するパターンです。


補足として、ポインタではなく、構造体の値を渡すことでうまくいきました。

ポインタを渡した場合、R/Wパニックを防ぐために、オブジェクトの周りにミューテックスロックを実装する必要があります。

メモリを共有して通信するのではなく、通信してメモリを共有してください。

チャネルやゴルーチンの周りにポインタではなく値を渡すことに固執し、メリットを享受してください。

9
eduncan911