Select 并发 阻塞 心跳 不能被垃圾回收 Ticker cannot be recovered by the garbage collector channel 通道
import ( "log" "sync" "time" ) func main() { ch := make(chan struct{}) task := func() { log.Println("IN task start ", time.Now()) time.Sleep(8 * time.Second) ch <- struct{}{} log.Println("IN task end ", time.Now()) } go task() log.Println("start", time.Now()) select { case <-time.After(16 * time.Second): log.Println("MAX ", time.Now()) case <-ch: log.Println("wait ", time.Now()) } log.Println("end ", time.Now()) }
ch := make(chan bool) go func() { log.Println("IN Receive", time.Now()) ch <- true }() go func() { log.Println("IN Send", time.Now()) <-ch }() log.Println("IN main", time.Now()) time.Sleep(4 * time.Second)
问题代码分析 all goroutines are asleep - deadlock! 死锁
import ( "log" "sync" "time" ) func main() { var wait func(wg *sync.WaitGroup) chan bool = func(wg *sync.WaitGroup) chan bool { wg.Wait() ch := make(chan bool) ch <- true return ch } var wg *sync.WaitGroup = &sync.WaitGroup{} wg.Add(1) task := func() { log.Println("IN task start ", time.Now()) time.Sleep(8 * time.Second) wg.Done() log.Println("IN task end ", time.Now()) } go task() log.Println("start", time.Now()) select { case <-time.After(16 * time.Second): log.Println("MAX ", time.Now()) case b := <-wait(wg): log.Println("wait ", b, time.Now()) } log.Println("end ", time.Now()) }
bug修复
内存泄漏
Go\src\time\tick.go
fatal error: runtime: out of memory
内存溢出
执行任务
在任务执行方法中
加入心跳逻辑
// Go program to illustrate the // concept of select statement import ( "log" "time" ) // function 1 func portal1(channel1 chan string) { time.Sleep(3 * time.Second) channel1 <- "Welcome to channel 1" } // function 2 func portal2(channel2 chan string) { time.Sleep(9 * time.Second) channel2 <- "Welcome to channel 2" } func main() { // Creating channels R1 := make(chan string) R2 := make(chan string) // calling function 1 and // function 2 in goroutine go portal1(R1) go portal2(R2) tick := time.Tick(500 * time.Millisecond) log.Println("start", time.Now()) var breakFalg bool for { if breakFalg { break } select { case <-tick: log.Println("tick", time.Now()) // case 1 for portal 1 case op1 := <-R1: log.Println("op1", op1, time.Now()) breakFalg = true // case 2 for portal 2 case op2 := <-R2: log.Println("op2", op2, time.Now()) breakFalg = true } } log.Println("end ", time.Now()) }
package main import "fmt" func fibonacci(c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) }
go run main.go
2022/09/21 17:28:57 start 2022-09-21 17:28:57.779631 +0800 CST m=+0.002580601
2022/09/21 17:28:58 tick 2022-09-21 17:28:58.2797792 +0800 CST m=+0.502728801
2022/09/21 17:28:58 tick 2022-09-21 17:28:58.7816052 +0800 CST m=+1.004554801
2022/09/21 17:28:59 tick 2022-09-21 17:28:59.2804567 +0800 CST m=+1.503406301
2022/09/21 17:28:59 tick 2022-09-21 17:28:59.780179 +0800 CST m=+2.003128601
2022/09/21 17:29:00 tick 2022-09-21 17:29:00.2813679 +0800 CST m=+2.504317501
2022/09/21 17:29:00 tick 2022-09-21 17:29:00.7800094 +0800 CST m=+3.002959001
2022/09/21 17:29:00 op1 Welcome to channel 1 2022-09-21 17:29:00.7801695 +0800 CST m=+3.003119101
2022/09/21 17:29:00 end 2022-09-21 17:29:00.7807681 +0800 CST m=+3.003717701
参考
// Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build windows // +build windows package main import ( "fmt" "strings" "time" "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/debug" "golang.org/x/sys/windows/svc/eventlog" ) var elog debug.Log type myservice struct{} func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) { const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue changes <- svc.Status{State: svc.StartPending} fasttick := time.Tick(500 * time.Millisecond) slowtick := time.Tick(2 * time.Second) tick := fasttick changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} loop: for { select { case <-tick: beep() elog.Info(1, "beep") case c := <-r: switch c.Cmd { case svc.Interrogate: changes <- c.CurrentStatus // Testing deadlock from https://code.google.com/p/winsvc/issues/detail?id=4 time.Sleep(100 * time.Millisecond) changes <- c.CurrentStatus case svc.Stop, svc.Shutdown: // golang.org/x/sys/windows/svc.TestExample is verifying this output. testOutput := strings.Join(args, "-") testOutput += fmt.Sprintf("-%d", c.Context) elog.Info(1, testOutput) break loop case svc.Pause: changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted} tick = slowtick case svc.Continue: changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} tick = fasttick default: elog.Error(1, fmt.Sprintf("unexpected control request #%d", c)) } } } changes <- svc.Status{State: svc.StopPending} return } func runService(name string, isDebug bool) { var err error if isDebug { elog = debug.New(name) } else { elog, err = eventlog.Open(name) if err != nil { return } } defer elog.Close() elog.Info(1, fmt.Sprintf("starting %s service", name)) run := svc.Run if isDebug { run = debug.Run } err = run(name, &myservice{}) if err != nil { elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err)) return } elog.Info(1, fmt.Sprintf("%s service stopped", name)) }
go\pkg\mod\golang.org\x\sys@v0.0.0-20220808155132-1c4a2a72c664\windows\svc\example\service.go
In Go language, the select statement is just like switch statement, but in the select statement, case statement refers to communication, i.e. sent or receive operation on the channel.
Syntax:
select{ case SendOrReceive1: // Statement case SendOrReceive2: // Statement case SendOrReceive3: // Statement ....... default: // Statement
Important points:
- Select statement waits until the communication(send or receive operation) is prepared for some cases to begin.
Example:
import ( "fmt" "time" ) // Go program to illustrate the // concept of select statement // function 1 func portal1(channel1 chan string) { time.Sleep(3 * time.Second) channel1 <- "Welcome to channel 1" } // function 2 func portal2(channel2 chan string) { time.Sleep(9 * time.Second) channel2 <- "Welcome to channel 2" } // main function func main() { // Creating channels R1 := make(chan string) R2 := make(chan string) // calling function 1 and // function 2 in goroutine go portal1(R1) go portal2(R2) select { // case 1 for portal 1 case op1 := <-R1: fmt.Println(op1) // case 2 for portal 2 case op2 := <-R2: fmt.Println(op2) } }
Output:
Welcome to channel 1
Explanation: In the above program, portal 1 sleep for 3 seconds and portal 2 sleep for 9 seconds after their sleep time over they will ready to proceed. Now, select statement waits till their sleep time, when the portal 2 wakes up, it selects case 2 and prints “Welcome to channel 1”. If the portal 1 wakes up before portal 2 then the output is “welcome to channel 2”.
If a select statement does not contain any case statement, then that select statement waits forever.
Syntax:
select{}
Example:
// main function func main() { select{ } }
go run main.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]:
The default statement in the select statement is used to protect select statement from blocking. This statement executes when there is no case statement is ready to proceed.
Example:
import "fmt" // main function func main() { // creating channel mychannel := make(chan int) select { case <-mychannel: default: fmt.Println("Not found") } }
The blocking of select statement means when there is no case statement is ready and the select statement does not contain any default statement, then the select statement block until at least one case statement or communication can proceed.
Example:
// main function func main() { // creating channel mychannel:= make(chan int) // channel is not ready // and no default case select{ case <- mychannel: } }
go run main.go
fatal error: all goroutines are asleep - deadlock!
In select statement, if multiple cases are ready to proceed, then one of them can be selected randomly.
Example:
// Go program to illustrate the // concept of select statement import "fmt" // function 1 func portal1(channel1 chan string) { for i := 0; i <= 3; i++ { channel1 <- "Welcome to channel 1" } } // function 2 func portal2(channel2 chan string) { channel2 <- "Welcome to channel 2" } // main function func main() { // Creating channels R1 := make(chan string) R2 := make(chan string) // calling function 1 and // function 2 in goroutine go portal1(R1) go portal2(R2) // the choice of selection // of case is random select { case op1 := <-R1: fmt.Println(op1) case op2 := <-R2: fmt.Println(op2) } }
随机返回
2022/09/21 16:56:31 start 2022-09-21 16:56:31.0583941 +0800 CST m=+0.003470401
2022/09/21 16:56:35 4 2022-09-21 16:56:35.0754892 +0800 CST m=+4.020565501
2022/09/21 16:56:35 end 2022-09-21 16:56:35.0754892 +0800 CST m=+4.020565501