go - syscall/js websocket 回调在非阻塞行为中不响应
问题描述
我有两个进程正在运行,用 Go 编写,其中一个(sender.go)向另一个(listener.go)发送消息,同时通过 websockets 卡在 for 循环中。
问题是 listener.go 仅在终止循环后才意识到它收到了消息。
我已经尝试了几个 websocket 库,我什至尝试过使用常规的 tcp 流,但是在 webassembly 中编译时它不起作用,因为浏览器不支持它。尽管有这种行为,但 syscall/js websocket 似乎是最合适的。
这里是 listener.go
func registerCallbacks(ws js.Value) {
ws.Call("addEventListener", "message", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
message := args[0].Get("data").String()
fmt.Println("message received ")
fmt.Println(message)
return nil
}))
}
func main() {
c := make(chan struct{}, 0)
toIP := "127.0.0.1"
toPort := 8081
ws := js.Global().Get("WebSocket").New(fmt.Sprintf("ws://%s:%d/ws", toIP, toPort))
registerCallbacks(ws)
const bigNum uint64 = 100000 * 10000
cb := func(this js.Value, args []js.Value) interface{} {
for i := uint64(0); i < bigNum; i++ {
doThings()
if i%10000000 == 0 {
fmt.Println("skimmed ten millions ")
}
}
fmt.Println("Exited for loop !!")
return nil
}
// call cb() in script of index.html from a button
js.Global().Set("cb", js.FuncOf(cb))
<-c
}
sender.go 也是如此,
func main() {
toIP := "127.0.0.1"
toPort := 8081
ws := js.Global().Get("WebSocket").New(fmt.Sprintf("ws://%s:%d/ws", toIP, toPort))
time.Sleep(1 * time.Second)
fmt.Println("sending ")
ws.Call("send", js.ValueOf("msg"))
fmt.Println("Program exit ")
}
如果有人愿意在这里重现这个问题,那就是 websocket 服务器,它接收来自发送者的消息以将其转发给接收者,用 node.js 编写,只需复制并粘贴它,它适用于多个项目
const port = 8081;
const host = '127.0.0.1';
var WebSocketServer = require('websocket').server;
var http = require('http');
var server = http.createServer(function(request, response) {});
server.listen(port, host, () => {
console.log('WS Server is running on port ' + port + '.');
});
wsServer = new WebSocketServer({
httpServer: server
});
let sockets = [];
wsServer.on('request', function(request) {
var connection = request.accept(null, request.origin);
sockets.push(connection)
console.log("Connection with node initiated")
connection.on('message', function(message) {
console.log("message received "+message)
sockets.forEach(function(s, index, array) {
if (message.type === 'utf8') {
broadcastData = message.utf8Data
console.log("message is: "+ broadcastData)
if(s!= connection) {
console.log('send data to ' + s.socket.remotePort + ': ' + broadcastData);
s.sendUTF(broadcastData)
}
}
});
});
});
我期待 listener.go 在退出 for 循环之前接收消息
PS:使用 sleep 语句不是一个很好的帮助,因为当这个 for 循环在 js 回调中运行时,sleep 语句会输出一个恐慌。
解决方案
首先,请注意,如果您正在生成 wasm Go 代码,它不是多线程的,至少在今天(2019 年 9 月)不是。请参阅 如何在使用 golang 创建的 wasm 中实现多线程? 所以只有一个实际的执行线程。
然后,请记住,goroutines在任何可用的线程上协同执行多任务。由于只有一个线程,因此任何时候都只有一个 goroutine 在运行。如果您正在运行的 goroutine 没有将处理器让给其他 goroutine,那么其他 goroutine 将不会运行。
您可以隐式放弃处理器(例如,通过等待通道或互斥体)或显式放弃处理器,通过time.Sleep
或runtime.Gosched
。如果没有这样的调用,任何会注意到传入消息的 goroutine 都没有机会看到它,并且您的回调(将在这个或第三个 goroutine 中运行)永远不会被调用。
(如果您在本机运行,而不是在 wasm 中运行,您通常会获得更多线程,并且即使一个 CPU 处于紧密循环中,也能够让其他 CPU 运行其他任务。)
推荐阅读
- oracle - 我在 Oracle 的 DBA 面板中看不到表、视图或索引
- authentication - kubernetes 用户在 kubeconfig 和 basic-auth-file 中的区别?
- mysql - 如何将 mysql 结果行存储为包含表格格式字符串的变量?
- c - 目录条目和链表
- python - 这个程序有什么错误?
- r - 将具有范围或值的字符转换为 R 中仅包含值的向量
- python - Python Writerrow 到 CSV(从 For 循环输出到列中)
- c - 在“关于 size_t 和 ptrdiff_t”中解释这段话
- html - Vuejs背景图像不起作用
- lua - 通过一个名称/调用引用两个单独的变量