栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

Golang,goroutines:紧急情况:运行时错误:无效的内存地址

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Golang,goroutines:紧急情况:运行时错误:无效的内存地址

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 mainimport (    "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:/Gobingo.exe run -race C:/gopath/src/github.com/drathier/scratchpad/go/main/main.go[0xc0820403c0 0xc0820403c0 0xc0820403c0 0xc0820403c0 0xc0820403c0]==================WARNING: DATA RACEWrite by goroutine 6:  main.worker()      C:/gopath/src/github.com/drathier/scratchpad/go/main/main.go:15 +0x8aPrevious write by goroutine 8:  main.worker()      C:/gopath/src/github.com/drathier/scratchpad/go/main/main.go:15 +0x8aGoroutine 6 (running) created at:  main.main()      C:/gopath/src/github.com/drathier/scratchpad/go/main/main.go:45 +0x10cGoroutine 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



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/633225.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号