从Go通道获取值

huangapple go评论81阅读模式
英文:

Getting value from Go channel

问题

我有一个go协程,它正在监听TCP连接并将其发送到主循环的一个通道中。我之所以在go协程中这样做,是为了使这个监听非阻塞,并能够同时处理活动连接。

我使用了一个带有空默认情况的select语句来实现这一点,就像这样:

go pollTcpConnections(listener, rawConnections)

for {
    // 检查新连接(非阻塞)
    select {
    case tcpConn := <-rawConnections:
        currentCon := NewClientConnection()
        pendingConnections.PushBack(currentCon)
        fmt.Println(currentCon)
        go currentCon.Routine(tcpConn)
    default:
    }
   // ... 处理活动连接
}

这是我的pollTcpConnections例程:

func pollTcpConnections(listener net.Listener, rawConnections chan net.Conn) {
  for {
    conn, err := listener.Accept()  // 这个会阻塞,据我所知
    if(err != nil) {
        checkError(err)
    }
    fmt.Println("新连接")
    rawConnections<-conn
  }
}

问题是我从未收到这些连接。如果我以阻塞的方式进行,就像这样:

for {
    tcpConn := <-rawConnections
// ...
}

我会收到连接,但它会阻塞...我也尝试过缓冲通道,但情况依然如此。我在这里漏掉了什么?

英文:

I have a go-routine which is listening for TCP connections and send these on a channel back to the main loop. The reason I'm doing this in a go-routine is to make this listening non-blocking and be able to handle active connections simultaneously.

I have implemented this with a select statement with an empty default case like this:

go pollTcpConnections(listener, rawConnections)

for {
    // Check for new connections (non-blocking)
    select {
    case tcpConn := &lt;-rawConnections:
        currentCon := NewClientConnection()
        pendingConnections.PushBack(currentCon)
        fmt.Println(currentCon)
        go currentCon.Routine(tcpConn)
    default:
    }
   // ... handle active connections
}

Here is my pollTcpConnections routine:

func pollTcpConnections(listener net.Listener, rawConnections chan net.Conn) {
  for {
    conn, err := listener.Accept()  // this blocks, afaik
    if(err != nil) {
        checkError(err)
    }
    fmt.Println(&quot;New connection&quot;)
    rawConnections&lt;-conn
  }
}

The problem is that I never recieve these connections. If I do it in a blocking way, like this:

for {
    tcpConn := &lt;-rawConnections
// ...
}

I recieve the connections, but it blocks... I have tried buffering the channel as well, but the same thing happens. What am I missing here?

答案1

得分: 1

根据现有的代码,很难确定为什么你没有看到任何连接。你的示例代码有一个问题,就是在select语句中有一个空的default情况,我们无法看到在这个for循环中发生了什么。按照你的写法,这个循环可能永远不会让出调度器。你基本上是在说“从通道中获取一个东西。没有?好的,重新开始。从通道中获取一个东西!”,但你实际上没有等待。当你执行某个阻塞你的goroutine时,该goroutine会让出调度器。因此,当你以正常方式进行通道读取时,如果没有要读取的值,该goroutine会被阻塞读取。由于它被阻塞,它也会让出调度器,以允许其他goroutine在底层线程上继续执行。我相当确定这就是为什么你的带有空的defaultselect会出错的原因;你导致该goroutine在for循环中无限循环,而从不让出调度器。

pendingConnections的角色不清楚,或者是否需要它。

从行为上无法确定checkError函数的作用。例如,它不会继续执行for循环,也不会退出。

无论如何,看起来这比它需要的要复杂。只需编写一个函数,将新连接作为其参数,并在连接时在新的goroutine中启动它。我总是这样写:

func handleConnection(c net.Conn) {
    // 在这里处理你的连接。
}

for {
    // 等待连接。
    conn, err := l.Accept()
    if err != nil {
        // 处理错误。你可能想要在这里中断或返回。
        break
    }
    // 在新的goroutine中处理每个连接
    go handleConnection(conn)
}

这基本上就是文档中所做的。

英文:

it's a little hard to tell why you're not seeing any connections based on the existing code. One problem with your sample is that you have an empty default case in a select statement, and then we can't see what else is happening in this for loop. The way you've written it, that loop might never yield to the scheduler. You're basically saying "get a thing from the channel. don't have one? ok, start over. get a thing from the channel!", but you never actually wait. When you do some action that blocks your goroutine, that goroutine yields to the scheduler. So when you do a channel read in the normal fashion, if there's no value to be read, that goroutine is blocked reading. Since it's blocked, it also yields to the scheduler to allow other goroutines to continue executing on the underlying thread. I'm fairly certain this is why your select with an empty default is breaking; you're causing that goroutine to loop infinitely on the for loop without ever yielding to the scheduler.

It's not clear what the role of pendingConnections is, or whether it's needed at all.

The other thing that's impossible to tell from the behavior is what your checkError function does. It doesn't, for example, continue to the top of the for loop, or bail.

Anyway, it looks like this is more complicated than it needs to be. Just have a function that takes the new connection as it's one parameter, and then launch that in a new goroutine when it connects. I always write it like this:

func handleConnection(c net.Conn) {
    // do something with your connection here.
}

for {
    // Wait for a connection.
    conn, err := l.Accept()
    if err != nil {
        // do something with your error.  You probably want to break or return here.
        break
    }
    // handle each connection in a new goroutine
    go handleConnection(conn)
}

this is more or less exactly what they do in the documentation.

huangapple
  • 本文由 发表于 2013年2月1日 21:21:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/14647226.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定