繁体   English   中英

Golang:函数类型的奇怪行为

[英]Golang: Strange behaviour with function type

显然,我的代码中存在竞争条件。 但我无法找到它,因为我很确定要正确同步。 经过几个小时的调试,你可能会帮我找到它。

首先,这是我的(非常简化的)代码:

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.parsep.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
}

那应该解决它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM