显然,我的 go 代码中存在竞争条件。但是我找不到它,因为我很确定可以正确同步。经过几个小时的调试,您可能可以帮我找到它。
首先,这是我的(非常简化的)代码:
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不是吗,虽然这两个是同时设置的?
并且这些东西应该使用 channel 同步 p.callbackSet ?!
完整的可运行示例位于 https://play.golang.org/p/14vn5Tie5Y
我尝试用一个更简单的函数替换主函数。我怀疑这个错误在 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
}
这应该可以解决问题。
关于戈朗 : Strange behaviour with function type,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37928704/
关闭。这个问题需要更多focused.它目前不接受答案。想改进这个问题吗?更新问题,使其只关注一个问题editingthispost.关闭3年前。Improvethisquestion告诉我如何使用Golang登录网站。下载xls文件是得到了,但是为了在Excel表格中有数据,需要登录网站。该站点位于公司的服务器上。如果你能告诉你怎么做。例如,我用来执行此操作的VBA代码。SetoFields=CreateObject("Scripting.Dictionary")WithoFields.Add"login","sdiscor".Add"password","sdiscor"EndWi
下面是我查询的代码:我有一个单维数组a当我打印a[0][0]时,我不明白为什么它返回字符a的ascii值:packagemainimport("fmt")funcmain(){a:=[3]string{"a","b","c"}fmt.Println(a[0][0])}输出:97 最佳答案 下面是如何打印ascii的代码示例a:=[3]string{"a","b","c"}for_,rune:=rangea{fmt.Println(rune)//Itwillprinta,b,c}因为你在你的代码中使用了[0][0],所以它是等价的fo
typepath[]bytefunc(ppath)ToUpper(){fori,b:=rangep{if'a'在上面(这个例子是从“TheGoBlog”复制过来的),如果ToUpper变成这样:func(ppath)ToUpper(){fori,_:=rangep{if'a'哪个会更有效率为什么?“TheGoBlog”对前一个说:“这里的ToUpper方法在forrange构造中使用两个变量来捕获索引和slice元素。这种形式的循环避免了在主体中多次写入p[i]。”什么意思? 最佳答案 前者有更多的内存操作,即在b上:它在循环的第一
我有一个用Golang编写的脚本,我不太明白。我想知道他为什么要在脚本里面写goserver.Start()?为什么不简单地编写server.Start?packagemainimport("github.com/miekg/dns""testing""time")constTEST_ADDR="127.0.0.1:9953"funcTestDNSResponse(t*testing.T){server:=NewDNSServer(&Config{dnsAddr:TEST_ADDR,})goserver.Start()//Allowsometimeforservertostarttim
我正在尝试在Go中做一些相对简单的事情——将字符串转换为整数,然后将其加倍:myInt,_:=strconv.Atoi(args[1])doubleArg:=myInt*2由于Atoi()返回两个参数(整数和err),我使用myInt,_:=来检索值的整数。我想将它加倍(因此是第二行)但不能在一行中完成所有操作:myInt,_:=strconv.Atoi(args[1])*2给我:multiple-valuestrconv.Atoi()insingle-valuecontext但是,根据我使用大多数其他语言的经验,必须在两行中执行此操作似乎有很多样板。这只是我必须处理的一个限制,还是有
我刚接触golang。尝试通过golang实现批量上传到Elasticsearch。我正在使用golang库->https://github.com/olivere/elastic用于与Elasticsearch通信。此外,我正在尝试一段示例代码,但出现以下错误...suresh@BLR-245:~/Desktop/tools/golang/src$goinstallgithub.com/crazyheart/elastic-bulk-upload#github.com/crazyheart/elastic-bulk-uploadgithub.com/crazyheart/elasti
我计划实现一个go-routine并有一个sync.WaitGroup同步创建的go-routine的结尾。我基本上使用go创建了一个线程.所以它是这样的:main(){varwgsync.WaitGroupfor{gomyThread(wg)wg.Add(1)}wg.wait()}myThread(wgsync.WaitGroup){deferwg.Done()}我之前曾与pthread_create合作过在某些情况下确实无法创建线程。在这种情况下,是否可能针对上述gomyThread(wg)无法启动和/或运行wg.Done()例程的其余部分是否正常运行?如果是这样,将报告什么以及如
Go的append()函数仅在给定slice的容量不足时分配新的slice数据(另请参见:https://stackoverflow.com/a/28143457/802833)。这可能会导致意外行为(至少对我这个golang新手来说):packagemainimport("fmt")funcmain(){a1:=make([][]int,3)a2:=make([][]int,3)b:=[][]int{{1,1,1},{2,2,2},{3,3,3}}common1:=make([]int,0)common2:=make([]int,0,12)//providesufficientcap
我需要编写一个测试来验证服务器响应。响应必须包含某些header和xml正文。首先,如何检查响应中是否存在所需的header。以及如何比较收到的XML和所需的XML。例如。响应必须包含header“Serv”。正文必须包含对象为“person”的xmlHTTP/1.1200OKConnection:Keep-AliveServ:"any-string"Content-Length:0Content-Type:text/xml;charset=UTF-8string-value我如何检查响应是否包含标题“Serv”并包含带有元素人和名称的正文xml 最佳答案
我四处搜索并没有找到另一个这样做的例子,但我无意中发现我能够通过简单地将另一个slice的片段传递给接受slice并返回它的函数来从另一个slice的片段创建一个sliceslice。例子:packagemainimport"fmt"funcmakeSliceFrom(s[]int)[]int{returns}funcmain(){s:=[]int{1,2,3,4,5,6,7,8,9,10}newS:=makeSliceFrom(s[1:7])fmt.Println(newS)}我不是在问这是否有效,因为我知道它有效并且似乎运作良好,我是在问这是否得到支持或有一些我不知道的不可预见的成