除非通过文件操作阻止通道通信,否则似乎不可能通过带有执行文件操作的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的引用进行锁定,但是会导致外部代码阻塞,直到挂起的读取完成为止,我特别希望能够中断正在进行的读取。
为了解决这种情况,标准库中的几个io.ReadCloser实现都支持并发调用Read和Close,其中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)将取消主体上的读取。