我对golang相当陌生,并尝试使用chanel理解主要原理并编写基于古洛丁的代码。
在我使用的其他html" target="_blank">语言中,没有这样的工具,我不知道会出现这样的错误,例如恐慌…
我的代码:
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
我如何确定问题出在哪里,如何很好地关闭古丁鱼,而不是将其留在流程中…
ps原谅我我的菜鸟问题。请
nil取消引用:
您正在尝试访问指针所引用的结构,但是该指针尚未设置为该结构的实例。您必须声明一个可以指向指针的结构。
错误首先出现在这里:
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
。这是等待一组goroutine完成运行然后再继续的简单方法。
首先,您告诉等待组它应该等待多少个goroutine。
wg.Add(3)
然后,您等待:
wg.Wait()
直到所有goroutine都调用了
wg.Done()
完成执行后会执行的操作。
在这种情况下,我们需要在所有工作程序执行完毕后关闭输出通道,以便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)
}
哪个输出:
[0x104382f0 0x104382f0 0x104382f0 0x104382f0 0x104382f0]
这可能不是您所期望的。但是,它的确突出了此代码的一个问题。以后再说。
如果要打印结构的内容,则可以停止使用指向的指针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
在通道下多次发送指向同一实例的指针,因此多个goroutine同时访问同一实例而无需同步。您可能想要的是停止使用指针,而使用值。Work
代替*Work
。
如果要使用指针,也许是因为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
,则无法确定实际值到底在那里。再一次,可以通过创建的多个实例*Work
或使用值代替指针:来轻松解决此问题Work
。
问题内容: 当运行我的Go程序时,它会慌张并返回以下内容: 我查看了其他人对相同异常的响应,但看不到任何简单的响应(即未处理的错误)。 我在无法访问代码中列出的API服务器的机器上运行它,但我希望它会返回适当的错误(因为我试图捕获这种错误)。 问题答案: 根据文档: “如果是由客户端策略(例如CheckRedirect)引起的,或者存在HTTP协议错误,则会返回错误。非2xx响应不会导致错误。 当
问题内容: 我正在尝试使用mgo将objectid字符串转换为bson ObjectId格式, idk为什么,但是如果我输入了错误/无效的输入字符串,我的应用程序就会出现运行时恐慌 我该如何预防呢?谢谢 问题答案: 文档,如果您传递无效的对象ID将恐慌: ObjectIdHex从提供的十六进制表示形式返回ObjectId。 以无效的十六进制表示形式调用此函数将导致运行时恐慌。 请参见IsObjec
我是golang的新手,目前正在学习本教程和源代码-http://golang.org/doc/articles/wiki/part2.go 创建此文件后,我将 知道我做错了什么导致了这种明显的记忆损坏吗?
问题内容: 我正在Ubuntu 12.04 LTS机器上使用Go 1.2进行编码-使用LiteIDE(对此非常满意)。 我下载并安装了 go termbox软件包 -构建了demo keyboard.go-构建良好。 但是,当我从LiteIDE运行时,我会感到恐慌-所有termbox演示都发生了同样的情况: 任何线索都将有所帮助。 问题答案: 是的,这是完全合理的: 交互式 终端应用程序需要 终端
我正在尝试实现简单的grpc服务器和客户端模型,只使用服务器端流的方法。我正在参考Official repo中的路线指南示例。尝试运行时,我从服务器端收到此错误 这是客户端的 服务器去 一些原型文件 我正在研究堆栈溢出中关于零指针错误的几个问题,我发现这段代码是罪魁祸首 我不知道如何解决这个问题,我怀疑我实施它的方式是错误的。任何帮助都将是巨大的。谢谢
问题内容: 我无法弄清楚我是否做了一些愚蠢的事情,或者是否发现了gorm中的错误。尽管我很清楚“无效的内存地址或nil指针取消引用”的含义,但是我完全不知道为什么它出现在这里。 简而言之,我打电话给我,没有明显原因,我感到恐慌。 我的代码的相关位: 在包中打开,并存储为包变量。这似乎不太干净,但似乎可以正常工作… 恐慌: …我的代码的第28行在哪里 我检查了gorm和函数中的注释行,但这并不是很明