import log
log.SetFlags(log.Lshortfile) // config.go:46 xxxxx
log.SetFlags(log.Ldate) // 2017/11/12 xxxx
log.Println("hello world") // 日志级别
log.Fatal("hello world") // 终止还行
// 在init函数中设置
func init() {
flag.StringVar(&httpMethod, "X", "GET", "HTTP method to use")
flag.StringVar(&postBody, "d", "", "the body of a POST or PUT request; from file use @filename")
flag.BoolVar(&followRedirects, "L", false, "follow 30x redirects")
flag.BoolVar(&onlyHeader, "I", false, "don't read body of request")
flag.BoolVar(&insecure, "k", false, "allow insecure SSL connections")
flag.Var(&httpHeaders, "H", "set HTTP header; repeatable: -H 'Accept: ...' -H 'Range: ...'")
flag.BoolVar(&saveOutput, "O", false, "save body as remote filename")
flag.StringVar(&outputFile, "o", "", "output file for body")
flag.BoolVar(&showVersion, "v", false, "print version number")
flag.StringVar(&clientCertFile, "E", "", "client cert file for tls config")
flag.BoolVar(&fourOnly, "4", false, "resolve IPv4 addresses only")
flag.BoolVar(&sixOnly, "6", false, "resolve IPv6 addresses only")
flag.Usage = usage
}
func usage() {
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] URL\n\n", os.Args[0])
fmt.Fprintln(os.Stderr, "OPTIONS:")
flag.PrintDefaults() # 使用选项
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, "ENVIRONMENT:")
fmt.Fprintln(os.Stderr, " HTTP_PROXY proxy for HTTP requests; complete URL or HOST[:PORT]")
fmt.Fprintln(os.Stderr, " used for HTTPS requests if HTTPS_PROXY undefined")
fmt.Fprintln(os.Stderr, " HTTPS_PROXY proxy for HTTPS requests; complete URL or HOST[:PORT]")
fmt.Fprintln(os.Stderr, " NO_PROXY comma-separated list of hosts to exclude from proxy")
}
func main() {
flag.Parse()
args := flag.Args()
if len(args) != 1 {
flag.Usage()
os.Exit(2)
}
}
package main
import (
"time"
)
func main() {
ch := make(chan int, 10)
go func() {
var i = 1
for {
i++
ch <- i
}
}()
for {
select {
case x := <- ch:
println(x)
case <- time.After(3 * time.Minute):
println(time.Now().Unix())
}
}
}
在for循环每次select的时候,都会实例化一个一个新的定时器。该定时器在3分钟后,才会被激活,但是激活后已经跟select无引用关系,被gc给清理掉。换句话说,被遗弃的time.After定时任务还是在时间堆里面,定时任务未到期之前,是不会被gc清理的。每次循环实例化的新定时器对象需要3分钟才会可能被GC清理掉,如果我们把上面复现代码中的3分钟改小点,改成10秒钟,通过top命令会发现大概10秒钟后,该程序占用的内存增长到1.05G后基本上就不增长了。
package main
import (
"time"
)
func main() {
ch := make(chan int, 10)
go func() {
for {
ch <- 100
}
}()
idleDuration := 3 * time.Minute
idleDelay := time.NewTimer(idleDuration)
defer idleDelay.Stop()
for {
idleDelay.Reset(idleDuration)
select {
case x := <- ch:
println(x)
case <-idleDelay.C:
return
}
}
}
31 // leak is a buggy function. It launches a goroutine that
32 // blocks receiving from a channel. Nothing will ever be
33 // sent on that channel and the channel is never closed so
34 // that goroutine will be blocked forever.
35 func leak() {
36 ch := make(chan int)
37
38 go func() {
39 val := <-ch
40 fmt.Println("We received a value:", val)
41 }()
42 }
像这种go routine泄漏非常明显,因为 val 从channel中读取,实际上channel不会有任何机会写,所以会泄漏。上面的版本比较容易看出,实际上,我们可以在生产环境中遇到类似的。
38 // result wraps the return values from search. It allows us
39 // to pass both values across a single channel.
40 type result struct {
41 record string
42 err error
43 }
44
45 // process is the work for the program. It finds a record
46 // then prints it. It fails if it takes more than 100ms.
47 func process(term string) error {
48
49 // Create a context that will be canceled in 100ms.
50 ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
51 defer cancel()
52
53 // Make a channel for the goroutine to report its result.
54 ch := make(chan result)
55
56 // Launch a goroutine to find the record. Create a result
57 // from the returned values to send through the channel.
58 go func() {
59 record, err := search(term)
60 ch <- result{record, err}
61 }()
62
63 // Block waiting to either receive from the goroutine's
64 // channel or for the context to be canceled.
65 select {
66 case <-ctx.Done(): // 如果超时了,那么,将会ch将不会有任何机会写入了,那么goroutine将会泄漏。
67 return errors.New("search canceled")
68 case result := <-ch: // 这个地方,如果channel写入了,那么不会有问题。
69 if result.err != nil {
70 return result.err
71 }
72 fmt.Println("Received:", result.record)
73 return nil
74 }
75 }
修复:
53 // Make a channel for the goroutine to report its result.
54 // Give it capacity so sending doesn't block.
55 ch := make(chan result, 1)
当超时出现的时候,result将会放到channel中,这就不会出现泄漏。使用goroutine时,问自己:
-
什么时候goroutine终止
-
什么能够阻止其终止。
-
Never start a goroutine without knowing how it will stop,除非时明确用途的
输入的channel忘记close
5 // processRecords is given a slice of values such as lines
36 // from a file. The order of these values is not important
37 // so the function can start multiple workers to perform some
38 // processing on each record then feed the results back.
39 func processRecords(records []string) {
40
41 // Load all of the records into the input channel. It is
42 // buffered with just enough capacity to hold all of the
43 // records so it will not block.
44
45 total := len(records)
46 input := make(chan string, total)
47 for _, record := range records {
48 input <- record
49 }
50 // close(input) // What if we forget to close the channel?
51
52 // Start a pool of workers to process input and send
53 // results to output. Base the size of the worker pool on
54 // the number of logical CPUs available.
55
56 output := make(chan string, total)
57 workers := runtime.NumCPU()
58 for i := 0; i < workers; i++ {
59 go worker(i, input, output)
60 }
61
62 // Receive from output the expected number of times. If 10
63 // records went in then 10 will come out.
64
65 for i := 0; i < total; i++ {
66 result := <-output
67 fmt.Printf("[result ]: output %s\n", result)
68 }
69 }
70
71 // worker is the work the program wants to do concurrently.
72 // This is a blog post so all the workers do is capitalize a
73 // string but imagine they are doing something important.
74 //
75 // Each goroutine can't know how many records it will get so
76 // it must use the range keyword to receive in a loop.
77 func worker(id int, input <-chan string, output chan<- string) {
78 for v := range input {
79 fmt.Printf("[worker %d]: input %s\n", id, v)
80 output <- strings.ToUpper(v)
81 }
82 fmt.Printf("[worker %d]: shutting down\n", id)
83 }
我们忘记对channel关掉,导致work routine得不到释放,对于buffered从channel,channel is only closed for sending, not receiving.