在Go中,如何关闭长时间运行的阅读?

Don*_*Gar 6 go goroutine

除非通过文件操作阻止通道通信,否则似乎不可能通过带有执行文件操作的goroutine的通道进行双向通信。我如何才能克服这种限制?

表达这个问题的另一种方式...

如果我在goroutine中运行类似于以下内容的循环,如何告诉它关闭连接并退出而不会阻塞下一个Read?

func readLines(response *http.Response, outgoing chan string) error {
    defer response.Body.Close()
    reader := bufio.NewReader(response.Body)

    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            return err
        }
        outgoing <- line
    }
}
Run Code Online (Sandbox Code Playgroud)

它无法从告诉它何时关闭的通道读取数据,因为它阻塞了网络读取(在我的情况下,这可能需要几个小时)。

从goroutine外部简单地调用Close()似乎并不安全,因为Read / Close方法似乎并不是完全线程安全的。

我可以简单地在例程的内部/外部使用对response.Body的引用进行锁定,但是会导致外部代码阻塞,直到挂起的读取完成为止,我特别希望能够中断正在进行的读取。

Cer*_*món 6

为了解决这种情况,标准库中的几个io.ReadCloser实现都支持并发调用ReadClose,其中Close会中断活动的Read。

net / http Transport创建的响应正文阅读器就是这些实现之一。在响应正文上同时调用Read和Close是安全的。

您还可以通过调用Transport CancelRequest方法来中断响应正文上的活动Read 。

这是在主体上使用close实现取消的方法:

func readLines(response *http.Response, outgoing chan string, done chan struct{}) error {
    cancel := make(chan struct{})
    go func() {
       select {
       case <-done:
          response.Body.Close()
       case <-cancel:
          return
    }()

    defer response.Body.Close()
    defer close(cancel) // ensure that goroutine exits

    reader := bufio.NewReader(response.Body)
    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            return err
        }
        outgoing <- line
    }
}
Run Code Online (Sandbox Code Playgroud)

从另一个goroutine调用close(done)将取消主体上的读取。