【发布时间】:2013-10-27 09:06:18
【问题描述】:
由于 go 是一种并发程序语言,因此使用channel(我几乎所有代码都使用它)或其他东西来同步goroutine。
而且我还知道 go 使用 scheduler 来调度 goroutine,这意味着你应该在每个 goroutine 中调用 scheduler(channel action, runtime.goSche 或其他东西) 并保证它会被执行。
以上是我目前对go 的所有限制,我使用它们来设计我的代码。
但我也发现它会在我的代码中发生代码阻塞。而且很难找到导致阻塞的原因(即使使用 GDB)。
我错过了什么吗?
还有什么可能导致阻塞?
我应该注意什么?
[编辑]:好的,因为我的项目的代码有点大。我决定不显示 标准 go 代码,只显示可能导致代码阻塞的部分的一般概念。以下是我项目中文件传输模块的一些代码片段:
func(c *client)listenRead() {
for {
_ := websocket.JSON.Receive(c.ws, &package)
switch package.Op {
case fileUpld:
c.fileMan.store <- package.Body
case fileDownld:
c.fileMan.downld <- package.Body
case c.xxx:
...
default:
// bad package.
}
}
}
一个client通过websocket从另一个client监听和接收消息,然后根据package的Op字段将包转发到不同的handler ,例如,fileManager 如下所示。
func(fm *fileManager) fileRouter() {
for {
select {
case fs := <-fm.fileUpld:
if window < filesize {
f.Write(fs.content) // client A write to file
window += fs.contSize
} else {
f.close() // close file
}
case fd := fm.downld:
go fm.downldFile(fd)
}
}
}
当chan fileUpld 获取从client 发送的数据时,fileManger 可以接收文件片段并存储到服务器。当它收到来自客户端的request 时,它可以下载文件,它会生成一个goroutine 来完成这项工作,如下所示:
func(fm *fileManager) downldFile(fd) {
f := getFile(fd) // get the file that client A write
b := make([]byte, SeqLength)
for {
if convergence < window {
f.Read(b)
// wrap 'b' to package 'p', for send
fm.server.send <- p // send to client B
} else if window < fileSize {
runtime.Goshed()
} else {
// download done.
fm.done <- xx
return
}
}
}
我的文件传输模块的主要思想是客户端A想向客户端B发送文件,而不想等待来自client B的acceptance,这意味着它将首先存储到服务器,client B稍后将通过从服务器下载文件来获取文件。但可能客户端 B 下载文件,而客户端 A 上传文件。所以上传和下载之间需要同步。
我使用三个变量来实现:window、convergence、filesize。当client A上传(写入)文件时,window会增加它写入的字节数。而当client B下载(读取)文件时,convergence会增加它读取的字节数。 Write 仅发生 window < filesize 而 read 仅发生 convergence < window。就像:
+-----------+---------------+--------------+
|############|||||||||||||||| |
+-----------+---------------+--------------+
^ ^ ^
| | |
convergence window filesize
我知道这会导致数据争用,但它也保证read 不会读取write 尚未写入的内容。(顺便说一句:我没有使用channel 来实现它的解决方案。)
当我尝试同时上传和下载时发生代码阻塞(我放慢了客户端A来实现它)。但是当我将GOMAXPROCS设置为2时就没有问题了。
【问题讨论】:
-
如果您有一个代码示例来演示您正在谈论的问题,那么您会更容易理解您的要求。如果您可以让示例在 play.golang.org 上运行,那就更好了。
-
@MatrixForg :我重新编辑并添加了代码示例。希望你更清楚。
标签: concurrency go