Передача значения в канал по какой-то причине блокирует поток - PullRequest
0 голосов
/ 25 июня 2018

Я использую канал для передачи сообщений от обработчика 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)

Почему это происходит?Если вы заметили, я запускаю слушателя в рутине.Я также понял, что HTTP-дескрипторы происходят в отдельном потоке.Если я удаляю вышеуказанную строку, поток становится разблокированным, и программа работает, как ожидалось.Что я делаю не так?

Чтобы уточнить, я хочу иметь возможность передавать тело запроса POST на канал.Справка.

ОБНОВЛЕНИЕ: Я читаю из канала в главном потоке:

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

Но поток все еще блокируется на той же строке.Для пояснения, нет ничего плохого в том, как я отправляю запрос.Если я удалю строку отправки канала выше, поток не блокируется.

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

Я думаю, @bereal дал хорошее объяснение по поводу использования небуферизованного или синхронного канала.

Еще один способ заставить вещи работать, это сделать буфер буферизованным, изменив строку, которая создает канал:

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

Это предотвратит блокировку функции.

0 голосов
/ 28 июня 2018

Я добавил недостающие части в ваш код и запустил его, все работает хорошо. Я не вижу ни одного блока. Вот код:

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
0 голосов
/ 25 июня 2018

Поскольку канал небуферизован , операция отправки блокируется до тех пор, пока не найдется кто-то, кто готов от них получить.Создание буферизованного канала будет только откладывать блокировку, поэтому вам всегда нужно некоторое чтение программы.

Обновление до вашего обновления: поток управления программы будет выглядеть следующим образом:

  1. Серверначинает прослушивание
  2. main отправляет запрос и ждет ответа
  3. Сервер получает запрос и пытается записать в канал
  4. main читает из канала

4 может произойти только после 2, который заблокирован 3, который заблокирован, потому что 4 еще не происходит.Классический тупик.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...