将值传递给通道由于某种原因阻塞了线程

时间:2018-06-25 05:38:57

标签: go

我正在使用一个通道来传递来自HTTP处理程序的消息:

package server

import (
    "bytes"
    "errors"
    "io/ioutil"
    "log"
    "net/http"
)

type Server struct {}

func (s Server) Listen() chan interface{} {
    ch := make(chan interface{})
    http.HandleFunc("/", handle(ch))
    go http.ListenAndServe(":8080", nil)
    return ch
}

func handle(ch chan interface{}) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        b, err := ioutil.ReadAll(r.Body)
        defer r.Body.Close()
        if err != nil {
            ch <- errors.New(string(500))
            return
        }
        w.Write([]byte("Hello World"))
        log.Print("about to pass to handler channel")
        ch <- bytes.NewBuffer(b)
        log.Print("passed to handler channel")
    }
} 

当我向运行在8080端口上的服务器发出请求时,线程在此行阻塞:

ch <- bytes.NewBuffer(b)

为什么会这样?如果您注意到,我将在goroutine中运行侦听器。我还发现HTTP处理是在单独的线程中发生的。如果删除上面的行,则该线程将被解除阻塞,程序将按预期工作。我在做什么错了?

为了澄清,我希望能够将POST请求的主体传递给通道。帮助。

更新: 我正在从主线程上的通道中读取内容:

listenerChan := n.Listen()
go SendRequest("POST", "http://localhost:8080", []byte("hello"))
for listenedMsg := range listenerChan {
    log.Print("listened message>>>> ", listenedMsg)
}

但是线程仍然在同一行上阻塞。为了澄清起见,我如何发送请求没有错。如果我删除上面的通道发送行,则该线程不会阻塞。

3 个答案:

答案 0 :(得分:2)

由于频道为unbuffered,因此发送操作将一直阻塞,直到有人准备好从中接收。对通道进行缓冲只会延迟阻塞,因此您始终需要一些读取goroutine。

更新到您的更新:程序的控制流将如下所示:

  1. 服务器开始监听
  2. main发送请求并等待响应
  3. 服务器收到请求并尝试写入通道
  4. main从频道中读取

4可能仅在2之后发生,而2被3阻止了,因为3尚未发生,所以3被阻止了。经典的僵局。

答案 1 :(得分:0)

我认为@bereal为使用无缓冲同步通道提供了很好的解释。

使工作正常进行的另一种方法是通过将创建通道的行更改为以下方式来缓冲通道:

ch := make(chan interface{}, 1)   // added the 1

这将防止该功能被阻止。

答案 2 :(得分:0)

我在代码中添加了缺少的部分并运行它,一切正常。我没看到任何障碍。这是代码:

package main

import (
    "bytes"
    "errors"
    "io/ioutil"
    "log"
    "net/http"
    "time"
)

type Server struct{}

func (s *Server) Listen() chan interface{} {
    ch := make(chan interface{})
    http.HandleFunc("/", handle(ch))
    go http.ListenAndServe(":8080", nil)
    return ch
}

func handle(ch chan interface{}) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        b, err := ioutil.ReadAll(r.Body)
        defer r.Body.Close()
        if err != nil {
            ch <- errors.New(string(500))
            return
        }
        w.Write([]byte("Hello World"))
        log.Print("about to pass to handler channel")
        ch <- bytes.NewBuffer(b)
        log.Print("passed to handler channel")
    }
}

// SendRequest send request
func SendRequest(method string, url string, data []byte) {
    tr := &http.Transport{
        MaxIdleConns:       10,
        IdleConnTimeout:    30 * time.Second,
        DisableCompression: true,
    }
    client := &http.Client{Transport: tr}
    reader := bytes.NewReader(data)
    req, err := http.NewRequest(method, url, reader)
    if err != nil {
        panic(err)
    }
    client.Do(req)
}

func main() {
    n := new(Server)
    listenerChan := n.Listen()
    go SendRequest("POST", "http://localhost:8080", []byte("hello"))
    for listenedMsg := range listenerChan {
        log.Print("listened message>>>> ", listenedMsg)
    }
}

输出为:

2018/06/28 17:22:10 about to pass to handler channel
2018/06/28 17:22:10 passed to handler channel
2018/06/28 17:22:10 listened message>>>> hello