[英]Go improper struct initialization?
雖然編碼我遇到了問題。 當我在goroutine中使用內部結構的方法時,我在這段代碼中看不到內部狀態。
package main
import (
"fmt"
"time"
)
type Inner struct {
Value int
}
func (c Inner) Run(value int) {
c.Value = value
for {
fmt.Println(c.Value)
time.Sleep(time.Second * 2)
}
}
type Outer struct {
In Inner
}
func (c Outer) Run() {
go c.In.Run(42)
for {
time.Sleep(time.Second)
fmt.Println(c.In)
}
}
func main() {
o := new(Outer)
o.Run()
}
程序打印:
from inner: {42}
from outer: {0}
from outer: {0}
from inner: {42}
from outer: {0}
from inner: {42}
from outer: {0}
from outer: {0}
也許這是指針問題,但我不知道如何解決它。
代碼中最明顯的錯誤是Inner.Run()
有一個值接收器,這意味着它獲得了Inner
類型的副本。 修改此項后,您將修改副本,並且調用者將看不到Inner
值的任何更改。
所以首先修改它以使用指針接收器:
func (c *Inner) Run(value int) {
// ...
}
如果方法具有指針接收器,則調用該方法的值的地址(指針)將傳遞給該方法。 在方法內部,您將修改指向的值,而不是指針。 指針指向調用者處出現的相同值,因此修改了相同的值(而不是副本)。
僅此更改可能會使您的代碼工作。 但是,程序的輸出是非確定性的,因為您從一個goroutine修改變量(字段),並且您也從另一個goroutine中讀取此變量,因此您必須以某種方式同步對此字段的訪問。
同步訪問的一種方法是使用sync.RWMutex
:
type Inner struct {
m *sync.RWMutex
Value int
}
創建Outer
值時,請初始化此互斥鎖:
o := new(Outer)
o.In.m = &sync.RWMutex{}
或者在一行中:
o := &Outer{In: Inner{m: &sync.RWMutex{}}}
當您訪問Inner.Value
字段時,在Inner.Run()
鎖定中:
func (c *Inner) Run(value int) {
c.m.Lock()
c.Value = value
c.m.Unlock()
for {
c.m.RLock()
fmt.Println(c.Value)
c.m.RUnlock()
time.Sleep(time.Second * 2)
}
}
當您訪問Outer.Run()
的字段時,您還必須使用鎖定:
func (c Outer) Run() {
go c.In.Run(42)
for {
time.Sleep(time.Second)
c.In.m.RLock()
fmt.Println(c.In)
c.In.m.RUnlock()
}
}
注意:
您的示例僅在Inner.Run
的開頭更改Inner.Value
一次。 所以上面的代碼執行了很多不必要的鎖定/解鎖,如果Outer.Run()
的循環等到設置值,則可以刪除這些鎖定/解鎖,之后兩個goroutine都可以讀取變量而不鎖定。 通常,如果變量也可以在以后更改,則在每次讀/寫時都需要上述鎖定/解鎖。
解決問題的最簡單方法是在Run
函數中使用指針接收器:
func (c *Inner) Run(value int) {
out = make(chan int)
c.Value = value
for {
fmt.Println(c.Value)
time.Sleep(time.Second * 2)
}
}
但另一種解決方案是使用out通道,您可以向其發送Inner
struct值:
func (c Inner) Run(value int) {
out = make(chan int)
c.Value = value
for {
fmt.Println(c.Value)
time.Sleep(time.Second * 2)
out <- c.Value
}
}
然后在一個單獨的goroutine中接收發送的值:
for{
go func() {
c.In.Run(42)
<-out
fmt.Println(out)
}()
time.Sleep(time.Second)
}
這是完整的代碼:
package main
import (
"fmt"
"time"
)
type Inner struct {
Value int
}
var out chan int
func (c Inner) Run(value int) {
out = make(chan int)
c.Value = value
for {
fmt.Println(c.Value)
time.Sleep(time.Second * 2)
out <- c.Value
}
}
type Outer struct {
In Inner
}
func (c Outer) Run() {
for{
go func() {
c.In.Run(42)
<-out
fmt.Println(out)
}()
time.Sleep(time.Second)
}
}
func main() {
o := new(Outer)
o.Run()
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.