Go   发布时间:2022-04-09  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了Golang:函数类型的奇怪行为大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
显然,我的代码存在竞争条件.但我无法找到它,因为我很确定要正确同步.经过几个小时的调试,你可能会帮我找到它.

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

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完全可运行的示例

我尝试用更简单的@L_556_4@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
}

那应该解决它.

大佬总结

以上是大佬教程为你收集整理的Golang:函数类型的奇怪行为全部内容,希望文章能够帮你解决Golang:函数类型的奇怪行为所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。