Go:与go频道一起出现死锁问题并选择

时间:2017-01-16 10:37:02

标签: go deadlock tcpserver

我在golang中实现了一个演示tcp聊天服务器,它工作正常,但每次用户断开连接时我都会尝试向广播频道写一条消息,让其他用户知道用户已将其断开连接,并且不进一步处理来自其他客户端的任何新消息,因为它是非缓冲通道

我已经通过代码评论并解释说你可以通过它,我不知道为什么代码块,我写的msgs

  1. 我要写一个频道
  2. 我已写入频道
  3. 我已从频道
  4. 中读到

    并且消息仍处于完美状态仍然是我的msg频道阻止。

    Ps:如果我使用缓冲通道代码没有阻塞,但我想知道代码卡在哪里。 我也尝试使用-race标志运行我的代码但没有帮助

    package main
    
    import (
        "fmt"
        "io"
        "net"
        "sync"
    )
    
    func main() {
        msg := make(chan string)          //broadcast channel (making it buffered channel the problem goes away)
        allConn := make(map[net.Conn]int) //Collection of incoming connections for broadcasting the message
        disConn := make(chan net.Conn)    //client disconnect channel
        newConn := make(chan net.Conn)    //new client connection channel
        mutext := new(sync.RWMutex)       //mux to assign unique id to incoming connections
        i := 0
        listener, err := net.Listen("tcp", "127.0.0.1:8081")
        checkErr(err)
        fmt.Println("Tcp server started at 127.0.0.1:8081")
        //Accept incoming connections and store them in global connection store allConn
        go func() {
            for {
                conn, err := listener.Accept()
                checkErr(err)
                mutext.Lock()
                allConn[conn] = i
                i++
                mutext.Unlock()
                newConn <- conn
            }
        }()
        for {
            select {
            //Wait for a new client message to arrive and broadcast the message
            case umsg := <-msg:
                fmt.Println("Broadcast Channel: Already Read")
                bmsg := []byte(umsg)
                for conn1, _ := range allConn {
                    _, err := conn1.Write(bmsg)
                    checkErr(err)
                }
    
            //Handle client disconnection [disConn]
            case conn := <-disConn:
                mutext.RLock()
                fmt.Println("user disconneting", allConn[conn])
                mutext.RUnlock()
                delete(allConn, conn)
                fmt.Println("Disconnect: About to Write")
                //this call results in deadlock even when channel is empty, buffered channel resolves the issue
                //need to know why
                msg <- fmt.Sprintf("Disconneting", allConn[conn])
                fmt.Println("Disconnect: Already Written")
    
            //Read client incoming message and put it on broadcasting channel and upon disconnect put on it disConn channel
            case conn := <-newConn:
                go func(conn net.Conn) {
                    for {
                        buf := make([]byte, 64)
                        n, err := conn.Read(buf)
                        if err != nil {
                            if err == io.EOF {
                                disConn <- conn
                                break
                            }
                        }
                        fmt.Println("Client: About to Write")
                        msg <- string(buf[0:n])
                        fmt.Println("Client: Already Written")
                    }
                }(conn)
                mutext.RLock()
                fmt.Println("User Connected", allConn[conn])
                mutext.RUnlock()
            }
        }
    }
    func checkErr(err error) {
        if err != nil {
            panic(err)
        }
    }
    

1 个答案:

答案 0 :(得分:4)

在Go中,无缓冲通道是&#34;同步点&#34;。也就是说,如果您有一个频道c,并执行c <- value,则goroutine会阻止,直到有人准备好v = <- c(并且相反的情况下,从阻塞频道接收而没有任何内容接收块直到值可用,但这可能不那么令人惊讶)。具体来说,对于阻塞通道,接收在发送完成之前完成。

由于你只有一个goroutine,它将无法循环回读取通道,写入将阻塞,直到可以读取。

理论上,您可以通过执行以下操作来解决这个问题:go func() { msg <- fmt.Sprintf("Disconneting", allConn[conn] }(),因此基本上会产生一个短暂的goroutine来进行写入。