Golang,goroutines:panic:运行时错误:无效的内存地址
我在golang中比较新,并试图理解主要原则并使用chanels编写基于gouroutines的代码。Golang,goroutines:panic:运行时错误:无效的内存地址
在其他LANGS,我用没有这样的仪器,我不知道像越来越恐慌这样的错误...
我的代码:
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)
}
但在终端我得到这个:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x0 pc=0x401130]
goroutine 8 [running]:
main.sendWork(0xc0820101e0)
C:/temp/gocode/src/helloA/helloA.go:21 +0x20
created by main.main
C:/temp/gocode/src/helloA/helloA.go:43 +0xe4
goroutine 1 [chan receive]:
main.receiveWork(0xc082010240, 0x0, 0x0, 0x0)
C:/temp/gocode/src/helloA/helloA.go:31 +0x80
main.main()
C:/temp/gocode/src/helloA/helloA.go:45 +0xf2
goroutine 5 [chan receive]:
main.worker(0xc0820101e0, 0xc082010240)
C:/temp/gocode/src/helloA/helloA.go:12 +0x55
created by main.main
C:/temp/gocode/src/helloA/helloA.go:40 +0xaf
goroutine 6 [runnable]:
main.worker(0xc0820101e0, 0xc082010240)
C:/temp/gocode/src/helloA/helloA.go:11
created by main.main
C:/temp/gocode/src/helloA/helloA.go:40 +0xaf
goroutine 7 [runnable]:
main.worker(0xc0820101e0, 0xc082010240)
C:/temp/gocode/src/helloA/helloA.go:11
created by main.main
C:/temp/gocode/src/helloA/helloA.go:40 +0xaf
我怎么能确定问题出在哪里,我怎么可以关闭gouroutines很好,不要把它们作为流程...
p.s.原谅我的noob问题。请
的无解引用:
您试图访问一个指针引用一个结构,但指针没有被设置为结构的实例。你必须声明一个可以指向指针的结构。
错误第一次出现在这里:
wo.x, wo.y, wo.z = 1,2,3
在您尝试写入对象通过wo
指向。但指针在这里没有;它实际上并不指向Work
的实例。我们必须创建这个实例,所以我们可以指出它。
指向结构体的指针的nil值为nil
。如果你没有声明一个指向它的结构实例,它指向nil。
var wo *Work
声明wo
作为Work
类型的指针nil
。
var wo = &Work{}
声明wo
为Work
类型的指针的Work
一个新的实例。
或者你可以使用更短的语法:
wo := &Work{}
至于僵局:
当我们关闭通道,范围循环通过该信道将退出。在func worker
我们通过频道进行排序。当这个频道关闭时,工作人员将退出。
为了等待所有工人完成处理,我们使用了sync.WaitGroup
。这是一个简单的方法来等待一组goroutines在继续之前完成运行。
首先你告诉waitgroup它应该等待多少个goroutines。
wg.Add(3)
那你就等着:
wg.Wait()
,直到所有的够程都要求
wg.Done()
它们执行完毕时,他们这样做。
在这种情况下,我们需要关闭输出通道时,所有的工人都执行完,从而使func receiveWork
可以退出其范围内循环。
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)
}
,输出:
[0x104382f0 0x104382f0 0x104382f0 0x104382f0 0x104382f0]
这可能是我们可以开始一个新的goroutine这个任务做到这一点不是你所期望的。但是,它强调了这个代码的一个问题。稍后更多。
如果你要打印的结构的内容,您既可以停止使用指针Work
,或环比片的元素,并打印出来一个接一个,比如这个:
for _, w := range data {
fmt.Printf("%v", w)
}
,输出:
&{1 2 3}&{1 2 3}&{1 2 3}&{1 2 3}&{1 2 3}
Go不跟随指针超过一个降压打印时,会造成死循环,所以你必须手动完成。
赛条件:
由于要发送指针的*Work
多次下降通道相同的情况下,同一实例是由多个在够程不同步的相同时间被访问。你可能想要的是停止使用指针,并使用值。 Work
而不是*Work
。
如果你想使用指针,也许是因为Work
实际上很大,你可能想要创建多个*Work
的实例,所以你只能将它发送到一个goroutine。
下面介绍一下go race detector不得不说的代码:
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
所有够程并发修改w.z
,所以如果他们尝试不同的值写入w.z
,但没有说明实际上最终会在哪里实现。 Work
:再次,这是很容易通过创建的*Work
多个实例,或者通过使用值而不是指针的固定。