Golang:函数类型的奇怪行为
发布时间:2020-12-16 09:24:35 所属栏目:大数据 来源:网络整理
导读:显然,我的代码中存在竞争条件.但我无法找到它,因为我很确定要正确同步.经过几个小时的调试,你可能会帮我找到它. 首先,这是我的(非常简化的)代码: package mainimport ( "log" "time")type Parser struct { callback Callback callbackSet chan bool test in
显然,我的代码中存在竞争条件.但我无法找到它,因为我很确定要正确同步.经过几个小时的调试,你可能会帮我找到它.
首先,这是我的(非常简化的)代码: package main import ( "log" "time" ) type Parser struct { callback Callback callbackSet chan bool test int } func NewParser() Parser { p := Parser{} p.test = 100 p.callbackSet = make(chan bool) return p } func (p *Parser) SetCallback(newCallback Callback) { log.Println("=> SET CALLBACK: ",newCallback) p.test = 100 p.callback = newCallback log.Println("=> SETTING CALLBACK DONE") p.callbackSet <- true } func (p *Parser) StartParsing() { go p.parse() } func (p *Parser) parse() { cb := <-p.callbackSet _ = cb log.Println("Verify Callback: ",p.callback) log.Println("Verify Test Variable: ",p.test) funcDone := make(chan bool) go func() { time.Sleep(3 * time.Second) // Some io-Operation here funcDone <- true }() _ = <-funcDone } type Callback func(Message) type Message int type Dialog struct { Parser Parser } func CreateDialog() (Dialog,error) { d := Dialog{} d.Parser = NewParser() d.Parser.StartParsing() return d,nil } func (d *Dialog) OnMessage(callback Callback) { log.Println("dialog.OnMessage: ",callback) time.Sleep(3 * time.Second) // This sleep is just to prove the synchronization. It could be removed. d.Parser.SetCallback(callback) } func main() { dialog,_ := CreateDialog() dialog.OnMessage(func(m Message){ log.Println("Message: ",m) }) time.Sleep(5 * time.Second) // Not clean but just to await all of the output } 现在最大的问题是:为什么p.callback< nil>在p.parse而p.test不是,虽然这两个是在同一时间设置的? 应该使用通道p.callbackSet来同步这些东西?! 在https://play.golang.org/p/14vn5Tie5Y处完全可运行的示例 我尝试用更简单的函数替换main函数.我怀疑该错误是在Dialog结构中的某个地方.当我绕过它的使用时,我无法重现这个问题: func main() { p := NewParser() p.StartParsing() p.SetCallback(func (m Message) { log.Println("Message: ",m) }) time.Sleep(5 * time.Second) // Not clean but just to await all of the output } 其余代码保持不变.这里修改(工作)版本的另一个可玩的例子:https://play.golang.org/p/0Y0nKbfcrv 解决方法
这是因为您按值存储Parser对象并从CreateDialog返回值Dialog.
当按值返回Dialog实例时,在CreateDialog中创建的原始Parser实例将丢失. 原始的Parser正在解析,并在记录时收到回调. func CreateDialog() (Dialog,error) { d := Dialog{} d.Parser = NewParser() d.Parser.StartParsing() // <-- this instance is parsing return d,nil } func main() { dialog,_ := CreateDialog() // dialog.Parser <-- this is now a new instance which is NOT parsing dialog.OnMessage(func(m Message){ log.Println("Message: ",m) }) } 因此,要修复它,您可以执行以下三种操作之一: 1)在main中调用StartParsing. func main() { dialog,_ := CreateDialog() dialog.Parser.StartParsing(); dialog.OnMessage(func(m Message){ log.Println("Message: ",m) }) } 2)将Parser存储为Dialog中的指针: func NewParser() *Parser { p := &Parser{} p.test = 100 p.callbackSet = make(chan bool) return p } type Dialog struct { Parser *Parser } 3)从CreateDialog返回Dialog作为指针: func CreateDialog() (*Dialog,error) { d := &Dialog{} d.Parser = NewParser() d.Parser.StartParsing() return d,nil } 那应该解决它. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |