Select 并发 阻塞 心跳 不能被垃圾回收 Ticker cannot be recovered by the garbage collector channel 通道

 

    tick := time.NewTicker(4000 * time.Millisecond)

    select {
    case i := <-data2send:
        log.Println(i)
        s := convertMsg(i)
        producer.Produce(&kafka.Message{
            TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny},
            Value:          []byte(*s),
        }, nil)
    case <-tick.C:
        log.Println("tick")
    }

 

 

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修复

    var breakFlag bool
    Ticker := time.NewTicker(4000 * time.Millisecond)
    for {
        if breakFlag {
            Ticker.Stop()
            break
        }
        select {
        case <-Ticker.C:
            log.Info("Ticker")
        case <-time.After(600 * OneSecond):
            log.Error("expected job runs at 600 second mark")
        case <-wait(wg):
        }
    }
 
Go\src\time\tick.go
// Copyright 2009 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.

package time

import "errors"

// A Ticker holds a channel that delivers “ticks” of a clock
// at intervals.
type Ticker struct {
    C <-chan Time // The channel on which the ticks are delivered.
    r runtimeTimer
}

// NewTicker returns a new Ticker containing a channel that will send
// the current time on the channel after each tick. The period of the
// ticks is specified by the duration argument. The ticker will adjust
// the time interval or drop ticks to make up for slow receivers.
// The duration d must be greater than zero; if not, NewTicker will
// panic. Stop the ticker to release associated resources.
func NewTicker(d Duration) *Ticker {
    if d <= 0 {
        panic(errors.New("non-positive interval for NewTicker"))
    }
    // Give the channel a 1-element time buffer.
    // If the client falls behind while reading, we drop ticks
    // on the floor until the client catches up.
    c := make(chan Time, 1)
    t := &Ticker{
        C: c,
        r: runtimeTimer{
            when:   when(d),
            period: int64(d),
            f:      sendTime,
            arg:    c,
        },
    }
    startTimer(&t.r)
    return t
}

// Stop turns off a ticker. After Stop, no more ticks will be sent.
// Stop does not close the channel, to prevent a concurrent goroutine
// reading from the channel from seeing an erroneous "tick".
func (t *Ticker) Stop() {
    stopTimer(&t.r)
}

// Reset stops a ticker and resets its period to the specified duration.
// The next tick will arrive after the new period elapses. The duration d
// must be greater than zero; if not, Reset will panic.
func (t *Ticker) Reset(d Duration) {
    if d <= 0 {
        panic("non-positive interval for Ticker.Reset")
    }
    if t.r.f == nil {
        panic("time: Reset called on uninitialized Ticker")
    }
    modTimer(&t.r, when(d), int64(d), t.r.f, t.r.arg, t.r.seq)
}

// Tick is a convenience wrapper for NewTicker providing access to the ticking
// channel only. While Tick is useful for clients that have no need to shut down
// the Ticker, be aware that without a way to shut it down the underlying
// Ticker cannot be recovered by the garbage collector; it "leaks".
// Unlike NewTicker, Tick will return nil if d <= 0.
func Tick(d Duration) <-chan Time {
    if d <= 0 {
        return nil
    }
    return NewTicker(d).C
}
 

内存泄漏  

Go\src\time\tick.go

fatal error: runtime: out of memory

内存溢出

 

执行任务

    var breakFlag bool
    tick := time.Tick(4000 * time.Millisecond)
    for {
        if breakFlag {
            break
        }
        select {
        case <-tick:
            log.Info("tick")
        case <-time.After(600 * OneSecond):
            log.Error("expected job runs at 600 second mark")
        case <-wait(wg):
        }
    }
 
注意:还有BUG

在任务执行方法中 

   defer wg.Done()
 
 

 

加入心跳逻辑

// 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())
}

  

 

 

A Tour of Go https://go.dev/tour/concurrency/5
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))
}

 

golang.org/x/sys/windows/svc

 

go\pkg\mod\golang.org\x\sys@v0.0.0-20220808155132-1c4a2a72c664\windows\svc\example\service.go

 
 Select Statement in Go Language - GeeksforGeeks https://www.geeksforgeeks.org/select-statement-in-go-language/
 

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)
	}
}

  

 随机返回

 

 

    log.Println("start", time.Now())
    select {
    case <-time.After(4 * time.Second):
        log.Println("4", time.Now())
    case <-time.After(16 * time.Second):
        log.Println("16", time.Now())
    }
    log.Println("end ", time.Now())
 

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

 

 

posted @ 2019-12-24 17:02  papering  阅读(410)  评论(0编辑  收藏  举报