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

似乎不可能通过通道与执行文件操作的 goroutine 进行双向通信,除非您阻止文件操作上的通道通信。我怎样才能解决这强加的限制?


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


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


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

    }

}

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


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


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


慕容森
浏览 234回答 1
1回答

慕斯709654

为了解决这种情况,标准库中的几个io.ReadCloser实现支持对Read和Close 的并发调用,其中 Close 中断了一个活动的 Read。由 net/http Transport创建的响应正文阅读器是这些实现之一。在响应主体上同时调用 Read 和 Close 是安全的。您还可以通过调用Transport CancelRequest 方法来中断响应正文上的活动读取。以下是如何在 body 上使用 close 实现取消:func readLines(response *http.Response, outgoing chan string, done chan struct{}) error {&nbsp; &nbsp; cancel := make(chan struct{})&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp;select {&nbsp; &nbsp; &nbsp; &nbsp;case <-done:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; response.Body.Close()&nbsp; &nbsp; &nbsp; &nbsp;case <-cancel:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; }()&nbsp; &nbsp; defer response.Body.Close()&nbsp; &nbsp; defer close(cancel) // ensure that goroutine exits&nbsp; &nbsp; reader := bufio.NewReader(response.Body)&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; line, err := reader.ReadString('\n')&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; outgoing <- line&nbsp; &nbsp; }}从另一个 goroutine 调用 close(done) 将取消对主体的读取。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go