doutongfu9484 2015-01-26 11:57
浏览 60

如何优雅地关闭频道?

I have a server , which deal the connection Session like this

type Session struct {
    conn *net.TCPConn //the tcp connection from client
    recvChan      chan []byte
    closeNotiChan chan bool 
    ok   bool
    lock sync.Mutex
}

func (sess *Session) Close() {
    sess.conn.Close()
    sess.lock.Lock()
    if sess.ok {
        sess.ok = false
        close(sess.closeNotiChan)
    }
    sess.lock.Unlock()
}

func (sess *Session) handleRecv() {
    defer func() {
        sess.Close()
    }()
    ch := sess.recvChan
    header := make([]byte, 2) 
    for {
        /**block until recieve len(header)**/
        n, err := io.ReadFull(sess.conn, header)
        if n == 0 && err == io.EOF {
            //Opposite socket is closed
            log.Warn("Socket Read EOF And Close", sess)
            break
        } else if err != nil {
            //Sth wrong with this socket
            log.Warn("Socket Wrong:", err)
            break
        }
        //body lenght
        size := binary.LittleEndian.Uint16(header) + 4

        data := make([]byte, size)

        n, err = io.ReadFull(sess.conn, t.Data)
        if n == 0 && err == io.EOF {
            log.Warn("Socket Read EOF And Close", sess)
            break
        } else if err != nil {
            log.Warn("Socket Wrong:", err)
            break
        }
        ch <- t //#1
    }
}

func (sess *Session) handleDispatch() {
    defer func() {
        sess.Close()
        close(sess.recvChan)//#2
        for msg := range sess.recvChan {
            //do something 
        }
    }()
    for {
        select {
        case msg, ok := <-sess.recvChan:
            if ok {
                //do something
            }
        case <-sess.closeNotiChan:
            return
        }
    }
}

func StartClient() {//deal a new connection 
    var client Session
    client.conn = connection
    client.recvChan = make(chan []byte, 64)
    client.closeNotiChan = make(chan bool)
    client.ok = true
    go client.handleRecv()
    client.handleDispatch()
}

receiving data and dispatching data are in different goroutines.Now I have a problem. When a connection close,there is a data race ,#1 and #2 ,something like the following(I don't post complete code)

WARNING: DATA RACE
Write by goroutine 179:
  runtime.closechan()
      /usr/local/go/src/runtime/chan.go:257 +0x0
  sanguo/gameserver.func·004()
      /data/mygo/src/sanguo/gameserver/session.go:149 +0xc7
  sanguo/gameserver.(*Session).handleDispatch()
      /data/mygo/src/sanguo/gameserver/session.go:173 +0x413
  sanguo/gameserver.(*Session).Start()
      /data/mygo/src/sanguo/gameserver/session.go:223 +0xc6
  sanguo/gameserver.handleClient()
      /data/mygo/src/sanguo/gameserver/gameserver.go:79 +0x43

Previous read by goroutine 126:
  runtime.chansend()
      /usr/local/go/src/runtime/chan.go:83 +0x0
  sanguo/gameserver.(*Session).handleRecv()
      /data/mygo/src/sanguo/gameserver/session.go:140 +0x1171

So, My problem is that how can I close the recvChan elegently and effectively ,without "data race" and panic.

  • 写回答

1条回答 默认 最新

  • douxiong3234 2015-01-26 12:17
    关注

    Channels synchronize goroutines via sending, but close on them isn't synchronized itself. What if you close a channel at the same time another goroutine was sending something through it?

    You should have another channel through which handleDispatch notifies the "owner" (ie. the writer) of sess.recvChan, namely handleRecv, that it wants to close the channel. Then handleRecv should stop sending from it and close it.

    Apart from that: I don't understand the line next to #2: for msg := range sess.recvChan { What are you trying to get there from sess.recvChan, if you just closed it?

    评论

报告相同问题?

悬赏问题

  • ¥15 对于相关问题的求解与代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料
  • ¥15 使用R语言marginaleffects包进行边际效应图绘制
  • ¥20 usb设备兼容性问题
  • ¥15 错误(10048): “调用exui内部功能”库命令的参数“参数4”不能接受空数据。怎么解决啊
  • ¥15 安装svn网络有问题怎么办