Web Golang,goroutines:死机:运行时错误:内存地址无效

Web Golang,goroutines:死机:运行时错误:内存地址无效,web,go,channel,goroutine,Web,Go,Channel,Goroutine,我在golang是个新手,试图理解主要原理,并使用香奈儿编写基于美食的代码 在我使用的其他语言中,没有这样的工具,我想知道是否会出现像恐慌这样的错误 我的代码: package main import "fmt" import ( "time" ) type Work struct { x,y,z int } func worker(in <-chan *Work, out chan<- *Work){ for w := range in {

我在golang是个新手,试图理解主要原理,并使用香奈儿编写基于美食的代码

在我使用的其他语言中,没有这样的工具,我想知道是否会出现像恐慌这样的错误

我的代码:

package main

import "fmt"
import (
    "time"
)
type Work struct {
    x,y,z int
}

func worker(in <-chan *Work, out chan<- *Work){
    for w := range in {
        w.z = w.x + w.y
        time.Sleep(time.Duration(w.z))
        out <-w
    }
}

func sendWork(in chan <- *Work){
    var wo *Work
    wo.x, wo.y, wo.z = 1,2,3
    in <- wo
    in <- wo
    in <- wo
    in <- wo
    in <- wo
}

func receiveWork(out <-chan *Work ) []*Work{
    var  slice []*Work
    for el := range out {
        slice = append(slice, el)
    }
    return slice
}

func main() {
    in, out := make(chan *Work), make(chan *Work)
    for i := 0; i<3; i++{
        go worker(in, out)
    }

    go sendWork(in)

    data := receiveWork(out)

    fmt.Printf("%v", data)
}
我怎样才能确定问题出在哪里,怎样才能很好地关闭gouroutines,而不是将它们作为进程


p、 请原谅我的问题。请

零解除引用:
您正试图访问指针引用的结构,但该指针尚未设置为该结构的实例。您必须声明一个可以将指针指向的结构

错误首先出现在此处:

wo.x, wo.y, wo.z = 1,2,3
尝试写入由
wo
指向的对象的位置。但这里的指针是零;它实际上并没有指向
工作
的实例。我们必须创建该实例,以便指向它

指向结构的指针的nil值为
nil
。如果您没有声明要指向的结构实例,它将指向nil

var wo *Work
wo
声明为
Work
类型的指针,指向
nil

 var wo = &Work{}
wo
声明为
Work
类型的指针,指向
Work
的新实例

也可以使用较短的语法:

wo := &Work{}
至于死锁:

当我们关闭一个通道时,该通道上的范围循环将退出。在
func worker
中,我们在通道上进行范围。当此通道关闭时,工人将退出

为了等待所有工人完成处理,我们使用
sync.WaitGroup
。这是一种简单的方法,可以等待一组goroutine完成运行后再继续

首先,告诉waitgroup它应该等待多少个goroutine

wg.Add(3)
然后你等待:

wg.Wait()
直到所有的Goroutine都打电话过来

wg.Done()
当他们完成执行时,他们会这样做

在这种情况下,我们需要在所有worker执行完毕后关闭输出通道,以便
func receiveWork
可以退出其for range循环。我们可以通过为此任务启动一个新的goroutine来实现这一点:

go func() {
    wg.Wait()
    close(out)
}()
这是经过以下编辑后的整个文件:

package main

import (
    "fmt"
    "sync"
    "time"
)

type Work struct {
    x, y, z int
}

func worker(in <-chan *Work, out chan<- *Work, wg *sync.WaitGroup) {
    for w := range in {
        w.z = w.x + w.y
        time.Sleep(time.Duration(w.z))
        out <- w
    }
    wg.Done() // this worker is now done; let the WaitGroup know.
}

func sendWork(in chan<- *Work) {
    wo := &Work{x: 1, y: 2, z: 3} // more compact way of initializing the struct
    in <- wo
    in <- wo
    in <- wo
    in <- wo
    in <- wo
    close(in) // we are done sending to this channel; close it
}

func receiveWork(out <-chan *Work) []*Work {
    var slice []*Work
    for el := range out {
        slice = append(slice, el)
    }
    return slice
}

func main() {
    var wg sync.WaitGroup
    in, out := make(chan *Work), make(chan *Work)
    wg.Add(3) // number of workers
    for i := 0; i < 3; i++ {
        go worker(in, out, &wg)
    }

    go sendWork(in)

    go func() {
        wg.Wait()
        close(out)
    }()

    data := receiveWork(out)

    fmt.Printf("%v", data)
}
这可能不是你所期望的。然而,它确实突出了这段代码的一个问题。稍后再谈

如果要打印结构的内容,可以停止使用指向
工作
的指针,或者循环切片的元素并逐个打印,如下所示:

for _, w := range data {
    fmt.Printf("%v", w)
}
哪些产出:

[0x104382f0 0x104382f0 0x104382f0 0x104382f0 0x104382f0]
&{1 2 3}&{1 2 3}&{1 2 3}&{1 2 3}&{1 2 3}
Go在打印时不会跟随指针一步多,以避免无限递归,因此您必须手动执行此操作

比赛条件:

由于您在通道中多次向
*Work
的同一实例发送指针,因此多个goroutine会同时访问同一实例,而无需同步。您可能希望停止使用指针,而使用值<代码>工作而不是
*工作

如果您想使用指针,可能是因为
Work
实际上非常大,您可能想创建
*Work
的多个实例,以便只将其发送到一个goroutine

下面是关于代码的说明:

C:/Go\bin\go.exe run -race C:/gopath/src/github.com/drathier/scratchpad/go/main/main.go
[0xc0820403c0 0xc0820403c0 0xc0820403c0 0xc0820403c0 0xc0820403c0]==================
WARNING: DATA RACE
Write by goroutine 6:
  main.worker()
      C:/gopath/src/github.com/drathier/scratchpad/go/main/main.go:15 +0x8a

Previous write by goroutine 8:
  main.worker()
      C:/gopath/src/github.com/drathier/scratchpad/go/main/main.go:15 +0x8a

Goroutine 6 (running) created at:
  main.main()
      C:/gopath/src/github.com/drathier/scratchpad/go/main/main.go:45 +0x10c

Goroutine 8 (running) created at:
  main.main()
      C:/gopath/src/github.com/drathier/scratchpad/go/main/main.go:45 +0x10c
==================
Found 1 data race(s)
exit status 66
在这一行:

w.z = w.x + w.y
所有goroutine都同时修改
w.z
,因此,如果它们试图将不同的值写入
w.z
,则无法知道实际结果是什么值。同样,通过创建多个
*工作
实例,或者使用值而不是指针:
工作
,可以很容易地解决这个问题

w.z = w.x + w.y