Golang中Channel未按预期工作的问题排查
Golang中Channel未按预期工作的问题排查 本地图形用户界面上有多个添加和删除按钮。一旦我从图形用户界面点击添加/删除按钮,就会通过“mainHandler”处理器以数组形式获取到活动按钮的ID。
然后,我从数组中提取每个ID并进行一些处理。对于每个ID,处理应定期(例如每60秒)持续进行。这将无限期地持续下去。
一旦我再次点击添加/删除按钮,我将再次获取所有活动ID的完整列表,并且只需要对这些ID进行处理。但有时,当我从本地图形用户界面添加/删除列表时,主线程没有被调用。
如果我注释掉 go c.func1() 函数,那么每当从本地图形用户界面更新数据时,我都能在处理器中接收到数据。
代码片段:
type DataTable struct {
Exit chan struct{}
DataList []string
}
var dataTable *DataTable
func (c *controller) mainHandler(mc xxxx) {
if dataTable == nil {
dataTable = &DataTable{}
} else {
dataTable.Exit <- struct{}{}
}
r := mc.BodyReader()
dataTable.DataList = r.GetSliceOfString("data") // 在切片中更新ID
go c.func1()
}
func (c *controller) func1() {
for {
select {
case <-time.After(60 * time.Second):
c.func2() // 处理将在该函数内对 dataTable.DataList 进行。有时此函数需要10秒来完成操作。
case <-dataTable.Exit:
return
}
}
}
更多关于Golang中Channel未按预期工作的问题排查的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你必须以 goroutine 的方式调用测试函数。否则程序会进入无限循环。
go testFunc() 有效吗?
请查看以下代码:
https://play.golang.org/p/t_lCaOIxKRs
当我发送 quit <- true 信号时,goroutine 应该立即停止,但它并没有停止。
如果程序中有任何错误,请告诉我。
这个用例的目的是立即停止 goroutine。
我认为“Go语言之旅”中的这段代码展示了@Gowtham_Girithar和@ani所说的关于go testFunc()的内容。
select语句会阻塞,直到其某个case可以运行,然后执行该case。如果有多个case准备就绪,它会随机选择一个。
处理将在此函数内的 dataTable.DataList 上进行。有时此函数需要 10 秒来完成操作。
不清楚我们是否在讨论“网页表格”。我使用 ag-Grid 而不是 dataTable。
如果我们讨论的是数据表格,我发现在这种情况下涉及 Go 会使很多事情变慢。我决定使用 AJAX 而不是 Go。使用 CSR 而不是 SSR。
问题出在通道 dataTable.Exit 的初始化时机和缓冲处理上。当 dataTable 首次创建时,Exit 通道未被初始化,导致后续向该通道发送数据时阻塞。同时,非缓冲通道在接收者未准备好的情况下会导致发送操作阻塞,这可能影响主线程的调用。
以下是修改后的代码:
type DataTable struct {
Exit chan struct{}
DataList []string
}
var dataTable *DataTable
func (c *controller) mainHandler(mc xxxx) {
if dataTable == nil {
dataTable = &DataTable{
Exit: make(chan struct{}), // 初始化通道
}
} else {
select {
case dataTable.Exit <- struct{}{}:
// 成功发送退出信号
default:
// 避免阻塞,丢弃多余的退出信号
}
}
r := mc.BodyReader()
dataTable.DataList = r.GetSliceOfString("data")
go c.func1()
}
func (c *controller) func1() {
defer func() {
// 清理资源
if dataTable != nil {
close(dataTable.Exit)
dataTable = nil
}
}()
for {
select {
case <-time.After(60 * time.Second):
c.func2() // 处理 dataTable.DataList
case <-dataTable.Exit:
return
}
}
}
关键修改点:
- 在创建
DataTable时初始化Exit通道 - 使用
select语句配合default分支避免通道发送操作阻塞 - 在
func1返回时关闭通道并清理资源
如果 func2 执行时间较长(如10秒),考虑使用带缓冲的通道并优化处理逻辑:
type DataTable struct {
Exit chan struct{}
DataList []string
mu sync.RWMutex // 添加互斥锁保护 DataList
}
func (c *controller) func1() {
ticker := time.NewTicker(60 * time.Second)
defer func() {
ticker.Stop()
close(dataTable.Exit)
dataTable = nil
}()
for {
select {
case <-ticker.C:
dataTable.mu.RLock()
list := make([]string, len(dataTable.DataList))
copy(list, dataTable.DataList)
dataTable.mu.RUnlock()
go c.func2(list) // 异步处理副本,避免阻塞定时器
case <-dataTable.Exit:
return
}
}
}
这样确保:
- 通道操作不会阻塞主线程
- 数据访问是线程安全的
- 长时间处理不会影响定时逻辑

