You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言中遍历net.Conn映射并调用Write时是否需要互斥锁?

关于Go中ConnectionsHandler的sendMessage系列方法锁的问题

核心结论:遍历connections map时必须加锁,原因和net.Conn.Write的线程安全无关,而是Go语言的map本身不支持并发读写/遍历。

具体分析

  1. Go map的并发安全性限制
    Go的原生map不是并发安全的——当你在sendMessageToAll中遍历map的同时,如果有其他goroutine执行addConnection或removeConnection(这两个操作已经加锁修改map),不加锁的遍历会直接触发panic。所以遍历整个map的过程必须被mutex保护。

  2. 关于net.Conn.Write的线程安全
    大部分标准库的net.Conn实现(比如TCPConn)的Write方法本身是线程安全的,但这不是你可以省略锁的理由。即使Write安全,遍历map的并发风险依然存在,而且在遍历过程中,如果某个连接被removeConnection关闭,Write会返回错误,但这属于业务错误处理,和锁的必要性无关。

推荐实现方式

为了平衡线程安全和并发性能,建议先在锁内复制一份连接列表,释放锁后再逐个写消息——这样锁的持有时间极短,不会阻塞add/remove操作:

sendMessageToAll 实现

func (c *ConnectionsHandler) sendMessageToAll(msg []byte) {
    c.mutex.Lock()
    // 复制当前所有连接到临时切片
    connList := make([]net.Conn, 0, len(c.connections))
    for _, conn := range c.connections {
        connList = append(connList, conn)
    }
    c.mutex.Unlock()

    // 遍历临时切片发送消息,此时不持有锁
    for _, conn := range connList {
        _, err := conn.Write(msg)
        if err != nil {
            // 处理写入错误,比如关闭连接并尝试移除
            conn.Close()
            // 注意:如果需要从map中移除,需调用removeConnection(它会自行加锁)
            // 这里需要你能通过conn关联到对应的id,比如可以在结构体中维护反向映射,或者封装conn时携带id
        }
    }
}

sendMessage 实现

给单个连接发消息时,同样需要先加锁获取连接,避免并发修改导致的读取异常:

import "fmt"

func (c *ConnectionsHandler) sendMessage(id string, msg []byte) error {
    c.mutex.Lock()
    conn, exists := c.connections[id]
    c.mutex.Unlock()

    if !exists {
        return fmt.Errorf("connection %s does not exist", id)
    }

    _, err := conn.Write(msg)
    if err != nil {
        // 写入失败时,调用removeConnection清理连接
        c.removeConnection(id)
        return err
    }
    return nil
}

总结

  • 只要涉及到对connections map的读取(遍历、单个获取),就必须加锁保护,防止并发修改导致的panic或数据不一致。
  • 复制连接列表再处理是优化手段,能减少锁的持有时间,提升整体并发效率。

内容的提问来源于stack exchange,提问作者Nikita Primak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 03:31:03