2 回答
TA贡献1850条经验 获得超11个赞
好的,发生的情况是当您尝试向通道添加内容时通道被阻塞。尝试done
使用缓冲区(我使用 1)初始化通道,如下所示:
done = make(chan bool, 1)
TA贡献1862条经验 获得超6个赞
您将看到OnConnectErrorHandler
在执行调用期间同步调用Connect()
. 在连接完全建立并且回调完成之前,该Connect()
函数不会启动单独的 goroutine 来处理 websocket 。OnConnected
因此,当您尝试写入无缓冲的通道时done
,您会阻塞最初调用该函数的同一个Goroutine,并且您自己会陷入僵局,因为没有 Goroutine 能够从通道读取数据来解锁您。run()
因此,您可以采用他的解决方案并将其转换为缓冲通道,这将起作用,但我的建议是不要针对这种一次性标志行为写入通道,而是使用信号发送close
。为每个要终止的条件定义一个通道run()
,并在相应的 websocket 处理函数中close
定义该条件发生时的通道。在底部run()
,您可以select
打开所有通道,并在第一个通道关闭时退出。它看起来像这样:
package main
import "errors"
func run(appName string) (err error) {
// first, define one channel per socket-closing-reason (DO NOT defer close these channels.)
connectErrorChan := make(chan struct{})
successDoneChan := make(chan struct{})
surpriseDisconnectChan := make(chan struct{})
// next, wrap calls to your handlers in a closure `https://gobyexample.com/closures`
// that captures a reference to the channel you care about
OnConnectErrorHandler := func(err error, socket gowebsocket.Socket) {
MyOnConnectErrorHandler(connectErrorChan, err, socket)
}
OnDisconnectedHandler := func(err error, socket gowebsocket.Socket) {
MyOnDisconectedHandler(surpriseDisconnectChan, err, socket)
}
// ... declare any other handlers that might close the connection here
// Do your setup logic here
// serviceURL, e := GetContext().getServiceURL(appName)
// . . .
// socket := gowebsocket.New(url)
socket.OnConnectError = OnConnectErrorHandler
socket.OnConnected = OnConnectedHandler
socket.OnTextMessage = socketTextMessageHandler
socket.OnDisconnected = OnDisconnectedHandler
// Prepare and send your message here...
// LogDebug("In %v func connecting to URL %v", methodName, url)
// . . .
// socket.SendText(jsonStr)
// now wait for one of your signalling channels to close.
select { // this will block until one of the handlers signals an exit
case <-connectError:
err = errors.New("never connected :( ")
case <-successDone:
socket.Close()
LogDebug("mission accomplished! :) ")
case <-surpriseDisconnect:
err = errors.New("somebody cut the wires! :O ")
}
if err != nil {
LogDebug(err)
}
return err
}
// *Your* connect error handler will take an extra channel as a parameter
func MyOnConnectErrorHandler(done chan struct{}, err error, socket gowebsocket.Socket) {
methodName := "OnConnectErrorHandler"
LogDebug("Starting %v parameters [err = %v , socket = %v]", methodName, err, socket)
LogInfo("Disconnected from server ")
close(done) // signal we are done.
}
这有几个优点:
1)你不需要猜测哪些回调发生在进程中,哪些回调发生在后台 goroutine 中(并且你不必让所有通道都缓冲“以防万一”)
2) 在多个通道上进行选择可以让您找出退出的原因,并可能以不同的方式处理清理或日志记录。
注意 1:如果您选择使用close信令,则必须为每个源使用不同的通道,以避免可能导致通道从不同的 goroutine 关闭两次的竞争条件(例如,当您返回响应时会发生超时,并且两个处理程序都会触发;关闭同一通道的第二个处理程序会导致panic。)这也是您不希望将defer close所有通道都放在函数顶部的原因。
注意2:与您的问题没有直接关系,但是 - 您不需要关闭每个通道 - 一旦它的所有句柄超出范围,无论通道是否已关闭,它都会被垃圾收集。
- 2 回答
- 0 关注
- 116 浏览
添加回答
举报