SoFunction
Updated on 2025-04-11

Go uses TimerController to solve the problem of too many timers

background

  • In Go, we need to start a goroutine to achieve timeout, but when I have a lot of tasks that require timeout control, I need to start a lot of goroutine, which is actually a kind of overhead and burden!
  • Sometimes when you need to register some Timers, you also need to build a large number of goroutines to achieve it. For example, I need to refresh a configuration asynchronously and listen to something asynchronously. At this time, the simple way is to use a large number of goroutines + timer/sleep to implement it!

Solution

MultiplexingIn fact, Go is also a timer implemented by multiplexing ideas, but it is the timer at the bottom, and there are too many timer problems that we need to solve!

Our idea is to implement a TimerController to help us manage many times and minimize overhead! Therefore, it can be achieved using a small top heap + Timer scheduler!

accomplish

Small top heap (minimum heap)

Use Go's owncontainer/heapImplementation Small Top Heap

import (
    "container/heap"
)

type HeapItem[T any] interface {
    Less(HeapItem[T]) bool
    GetValue() T
}

// Reference IntHeaptype heapQueue[T any] []HeapItem[T]

func (h heapQueue[T]) Len() int           { return len(h) }
func (h heapQueue[T]) Less(i, j int) bool { return h[i].Less(h[j]) }
func (h heapQueue[T]) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *heapQueue[T]) Push(x any) {
    // Push and Pop use pointer receivers because they modify the slice's length,
    // not just its contents.
    *h = append(*h, x.(HeapItem[T]))
}

func (h *heapQueue[T]) Pop() any {
    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[0 : n-1]
    return x
}

type HeapQueue[T any] struct {
    queue heapQueue[T]
}

func (h *HeapQueue[T]) ptr() *heapQueue[T] {
    return &
}

// NewHeapQueue is non-concurrency securityfunc NewHeapQueue[T any](items ...HeapItem[T]) *HeapQueue[T] {
    queue := make(heapQueue[T], len(items))
    for index, item := range items {
       queue[index] = item
    }
    (&queue)
    return &HeapQueue[T]{queue: queue}
}

func  (h *HeapQueue[T])  Push(item HeapItem[T]) { 
    ((), item)
}

func  (h *HeapQueue[T])  Pop() (T, bool ) { 
    if ().Len() == 0 {
       var Nil T
       return Nil, false
    }
    return (()).(HeapItem[T]).GetValue(), true
}

// The Peek method is used to return the top element of the heap without removing itfunc  (h *HeapQueue[T])  Peek() (T, bool ) { 
    if ().Len() > 0 {
       return [0].GetValue(), true
    }
    var Nil T
    return Nil, false
}

func (h *HeapQueue[T]) Len() int {
    return ().Len()
}

Scheduler

type Timer struct {
    Timeout    
    Name       string
    NotifyFunc func()
}

func (t *Timer) GetCurTimeout()  {
    return (())
}

// Notify todo support async notify
func (t *Timer) Notify() {
    if  != nil {
       ()
    }
}

func (t *Timer) IsExpired() bool {
    return (())
}

func (t *Timer) Less(v HeapItem[*Timer]) bool {
    return (().Timeout)
}

func (t *Timer) GetValue() *Timer {
    return t
}

type TimerController struct {
    timers  chan *Timer
    minHeap *HeapQueue[*Timer]

    closeOnce 
    close     chan struct{}
}

func (t *TimerController) AddTimer(timer *Timer) bool {
    if timer == nil {
       return false
    }
    select {
    case <-:
       return false
    default:
        <- timer
       return true
    }
}

func (t *TimerController) Close() {
    (func() { close() })
}

func NewTimerController(bufferSize int) *TimerController {
    return &TimerController{
       timers:  make(chan *Timer, bufferSize),
       minHeap: NewHeapQueue[*Timer](),
       close:   make(chan struct{}),
    }
}

func (t *TimerController) Start() {
    go t._start()
}
func (t *TimerController) _start() {
    const defaultTimeout =  * 24

    var (
       curMinTimer *Timer
       timeout     = (defaultTimeout)
    )
    for {
       select {
       case <-:
          close()
          ()
          return
       case timer := <-:
          (timer)
          curMinTimer, _ = ()
          (())
          //("-1 name: %s, timeout: %s\n", , ())
       case <-:
          if curMinTimer != nil {
             ()
             curMinTimer = nil
             ()
          }
          curMinTimer, _ = ()
          if curMinTimer == nil {
             (defaultTimeout)
             continue
          }
          (())
          //("-2 name: %s, timeout: %s\n", , ())
       }
    }
}

test

func TestTimerController(t *) {
    controller := NewTimerController(1024)
    ()
    defer ()
    now := ()
    arrs := make([]string, 0)
    NewTimer := func(num int) *Timer {
       return &amp;Timer{Timeout: ((num) * ), Name: (num), NotifyFunc: func() {
          arrs = append(arrs, (num))
       }}
    }
    // 8 timeser registered out of order here    (NewTimer(5))
    (NewTimer(6))
    (NewTimer(3))
    (NewTimer(4))
    (NewTimer(7))
    (NewTimer(8))
    (NewTimer(1))
    (NewTimer(2))

    ( * 1)
    ("%#v\n", arrs)
    // Finally we can get the sequential execution!    (t, arrs, []string{"1", "2", "3", "4", "5", "6", "7", "8"})
}




func TestTimerController_Stable(t *) {
    controller := NewTimerController(1024)
    ()
    defer ()
    now := ()
    arrs := make(map[string]bool, 0)
    NewTimer := func(num int, name string) *Timer {
       return &amp;Timer{Timeout: ((num) * ), Name: name, NotifyFunc: func() {
          arrs[name] = true
       }}
    }
    // We repeatedly register the timer for the same implementation execution, so the expected result and registration order of each execution are the same as that of the registration.    (NewTimer(2, "1"))
    (NewTimer(2, "2"))
    (NewTimer(2, "3"))
    (NewTimer(2, "4"))
    (NewTimer(2, "5"))

    ( * 1)
    ("%#v\n", arrs)
    (t, arrs, map[string]bool{"1": true, "2": true, "3": true, "4": true, "5": true})
}

The above is the detailed content of Go using TimerController to solve the problem of too many timers. For more information about Go TimerController to solve the problem of too many timers, please pay attention to my other related articles!