转到 : Deadlock issue with go channel and select

标签 go deadlock tcpserver

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

我已经按代码注释和解释了你可以通过它,我不知道为什么代码块,我写了消息

  1. 我要写信给 channel
  2. 我已经给 channel 写信了
  3. 我已从 channel 阅读

而且消息都井井有条,但我的消息 channel 仍然阻塞。

Ps:如果我使用缓冲 channel ,代码不会阻塞,但我想知道我的代码在哪里卡住了。 我还尝试使用 -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)
    }
}

最佳答案

在 Go 中,无缓冲 channel 是一个“同步点”。也就是说,如果你有一个 channel c ,然后做 c <- value , goroutine 阻塞直到有人准备好做 v = <- c (反之亦然,从阻塞 channel 接收数据时,在值可用之前没有任何东西可以接收 block ,但这可能并不令人惊讶)。具体来说,对于阻塞 channel ,接收在发送完成之前完成。

由于您只有一个 goroutine,它无法循环回从 channel 读取并且写入将阻塞,直到可以读取为止。

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

关于转到 : Deadlock issue with go channel and select,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41674415/

相关文章:

mongodb - 使用mgo在MongoDB中进行有效的分页

amazon-web-services - 如何在Golang中将AWS Xray跟踪添加到SMTP调用中

php session 文件死锁

sockets - 在服务器中的唯一一个套接字上处理多个 TCP 连接

forms - 如何为具有 gin(框架)和 golang 接口(interface)的表单制作通用表单函数?

go - 如何用 Go 实现 BitSet?

c# - 尝试在 sleep 循环中获取锁是否可以(避免死锁、饥饿等...)?

mysql - 如果不存在则插入行而不会出现死锁

python - 如何关闭 Python TCPServer 或 HTTPServer 或 SimpleHTTPServer?

sockets - 计算机上的 TCP 服务器。未建立连接