1 回答

TA貢獻1757條經(jīng)驗 獲得超8個贊
nil 取消引用:
您正在嘗試訪問由指針引用的結(jié)構(gòu),但該指針尚未設(shè)置為該結(jié)構(gòu)的實例。您必須聲明一個可以指向指針的結(jié)構(gòu)。
錯誤首先出現(xiàn)在這里:
wo.x, wo.y, wo.z = 1,2,3
您嘗試寫入指向的對象的位置wo。但是這里的指針為零;它實際上并不是指向Work. 我們必須創(chuàng)建該實例,以便我們可以指向它。
指向結(jié)構(gòu)體的指針的 nil 值為nil。如果你沒有為它聲明一個結(jié)構(gòu)體實例,它指向 nil。
var wo *Work
聲明wo為類型的指針Work到nil。
var wo = &Work{}
聲明wo為指向 的Work新實例的類型指針Work。
或者您可以使用較短的語法:
wo := &Work{}
至于死鎖:
當(dāng)我們關(guān)閉一個通道時,該通道上的范圍循環(huán)將退出。在func worker我們范圍內(nèi)的一個頻道。當(dāng)這個通道關(guān)閉時,worker(s) 將退出。
為了等待所有工人完成處理,我們使用sync.WaitGroup. 這是在繼續(xù)之前等待一組 goroutine 完成運行的簡單方法。
首先你告訴等待組它應(yīng)該等待多少個 goroutine。
wg.Add(3)
然后你等待:
wg.Wait()
直到所有的 goroutine 都調(diào)用了
wg.Done()
當(dāng)他們完成執(zhí)行時他們會這樣做。
在這種情況下,我們需要在所有 worker 執(zhí)行完畢后關(guān)閉輸出通道,以便func receiveWork可以退出其 for range 循環(huán)。我們可以通過為此任務(wù)啟動一個新的 goroutine 來做到這一點:
go func() {
wg.Wait()
close(out)
}()
這是整個文件,經(jīng)過這些編輯:
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]
這可能不是您所期望的。然而,它確實突出了此代碼的一個問題。稍后再談。
如果你想打印結(jié)構(gòu)體的內(nèi)容,你可以停止使用指向 的指針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 在打印時不會跟隨指針向下一步,以避免無限遞歸,因此您必須手動執(zhí)行此操作。
比賽條件:
由于您*Work在通道中多次發(fā)送指向同一個實例的指針,因此多個 goroutine 在沒有同步的情況下同時訪問同一個實例。您可能想要的是停止使用指針,而使用值。Work而不是*Work.
如果你想使用指針,也許因為Work它實際上非常大,你可能想要創(chuàng)建多個實例,*Work所以你只將它發(fā)送到一個 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,則無法知道實際最終會出現(xiàn)什么值。再一次,這很容易通過創(chuàng)建 的多個實例*Work或使用值而不是指針來解決:Work。
- 1 回答
- 0 關(guān)注
- 168 瀏覽
添加回答
舉報