我使用goroutines实现http.Get超时,然后我发现goroutines的数量一直在稳定增长,当达到1000左右时,程序将退出
码:
package main
import (
"errors"
"io/ioutil"
"log"
"net"
"net/http"
"runtime"
"time"
)
// timeout dialler
func timeoutDialler(timeout time.Duration) func(network, addr string) (net.Conn, error) {
return func(network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, timeout)
}
}
func timeoutHttpGet(url string) ([]byte, error) {
// change dialler add timeout support && disable keep-alive
tr := &http.Transport{
Dial: timeoutDialler(3 * time.Second),
DisableKeepAlives: true,
}
client := &http.Client{Transport: tr}
type Response struct {
resp []byte
err error
}
ch := make(chan Response, 0)
defer func() {
close(ch)
ch = nil
}()
go func() {
resp, err := client.Get(url)
if err != nil {
ch <- Response{[]byte{}, err}
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
ch <- Response{[]byte{}, err}
return
}
tr.CloseIdleConnections()
ch <- Response{body, err}
}()
select {
case <-time.After(5 * time.Second):
return []byte{}, errors.New("timeout")
case response := <-ch:
return response.resp, response.err
}
}
func handler(w http.ResponseWriter, r *http.Request) {
_, err := timeoutHttpGet("http://google.com")
if err != nil {
log.Println(err)
return
}
}
func main() {
go func() {
for {
log.Println(runtime.NumGoroutine())
time.Sleep(500 * time.Millisecond)
}
}()
s := &http.Server{
Addr: ":8888",
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
}
http.HandleFunc("/", handler)
log.Fatal(s.ListenAndServe())
}
http://play.golang.org/p/SzGTMMmZkI
用1而不是0初始化chan:
ch := make(chan Response, 1)
并删除关闭并延迟ch的延迟块。
参见:http :
//blog.golang.org/go-concurrency-patterns-timing-out-
and
我认为这是正在发生的事情:
我假设您正在设置,ch = nil
是因为在您这样做之前,您会遇到运行时恐慌,因为这是您尝试写入规范中所述的关闭通道时发生的情况。
给ch一个1的缓冲区意味着fetch go例程可以发送给它而无需接收者。如果处理程序由于超时而返回,则稍后将收集所有垃圾。
9.8. Goroutines和线程 在上一章中我们说goroutine和操作系统的线程区别可以先忽略。尽管两者的区别实际上只是一个量的区别,但量变会引起质变的道理同样适用于goroutine和线程。现在正是我们来区分开两者的最佳时机。 9.8.1. 动态栈 每一个OS线程都有一个固定大小的内存块(一般会是2MB)来做栈,这个栈会用来存储当前正在被调用或挂起(指在调用其它函数时)的函数的内部变量。
8.1. Goroutines 在Go语言中,每一个并发的执行单元叫作一个goroutine。设想这里的一个程序有两个函数,一个函数做计算,另一个输出结果,假设两个函数没有相互之间的调用关系。一个线性的程序会先调用其中的一个函数,然后再调用另一个。如果程序中包含多个goroutine,对两个函数的调用则可能发生在同一时刻。马上就会看到这样的一个程序。 如果你使用过操作系统或者其它语言提供的线程,那
并发程序指同时进行多个任务的程序,随着硬件的发展,并发程序变得越来越重要。Web服务器会一次处理成千上万的请求。平板电脑和手机app在渲染用户画面同时还会后台执行各种计算任务和网络请求。即使是传统的批处理问题--读取数据,计算,写输出--现在也会用并发来隐藏掉I/O的操作延迟以充分利用现代计算机设备的多个核心。计算机的性能每年都在以非线性的速度增长。 Go语言中的并发程序可以用两种手段来实现。本章
8.7. Goroutines Go语言中使用go可以启动一个goroutine。goroutine 和线程的概念类似,和程序共享一个地址空间。 goroutines和支持多路并发草组系统中的协程(coroutines)类似,用户不用关心具体 的实现细节。 func server(i int) { for { print(i) sys.sle
问题内容: 我试图了解Go中的并发性。特别是,我编写了以下线程不安全程序: 我认识到我应该使用渠道来防止与发生竞争,但这不是重点。程序打印,然后似乎永远循环(不再打印任何内容)。我希望它能打印出无限的数字列表,可能由于竞态条件而跳过某些数字并重复其他数字(或更糟的是,在更新数字时打印数字)。 我的问题是:为什么程序只打印一行? 只是要清楚一点:对于这个玩具示例,我不是故意使用渠道。 问题答案: 关
问题内容: 我想对列进行累加,但是每当遇到0时就重置聚合值 这是我尝试做的一个例子: 该数据集: 给出以下内容: 问题答案: 在SQL Server 2008中,由于无法使用分析函数,因此受到了严重限制。以下方法效率不高,但可以解决您的问题: las,在SQL Server 2012之前,最有效的解决方案可能涉及游标。在SQL Server 2012+中,您只需执行以下操作: