这个空的select-case-default代码块有什么效果?

huangapple go评论114阅读模式
英文:

What is the effects for this empty select-case-default code block?

问题

我正在尝试理解一个池库的代码,当实例化一个池结构体时,调用一个名为startCleanerLocked(t Duration)的函数,在这个函数中,有一个空的select...case...default...代码块,我无法理解这个代码块的作用。

Pool接口定义如下:

  1. // Pool接口。
  2. type Pool interface {
  3. Get(ctx context.Context) (io.Closer, error)
  4. Put(ctx context.Context, c io.Closer, forceClose bool) error
  5. Close() error
  6. }

List结构体实现了Pool接口,定义如下:

  1. type List struct {
  2. // New是一个由应用程序提供的用于创建和配置项目的函数。
  3. //
  4. // 从new返回的项目不能处于特殊状态
  5. // (订阅了pubsub频道,启动了事务等)。
  6. New func(ctx context.Context) (io.Closer, error)
  7. // mu保护下面定义的字段。
  8. mu sync.Mutex
  9. cond chan struct{}
  10. closed bool
  11. active int
  12. // 清理过期项目
  13. cleanerCh chan struct{}
  14. // 最近使用的项目堆栈,最近使用的项目在前面。
  15. idles list.List
  16. // Config池配置
  17. conf *Config
  18. }

当创建一个新的池时,会调用startCleanerLocked(t Duration)函数:

  1. // NewList创建一个新的池。
  2. func NewList(c *Config) *List {
  3. // 检查配置
  4. if c == nil || c.Active < c.Idle {
  5. panic("config nil or Idle Must <= Active")
  6. }
  7. // 创建池
  8. p := &List{conf: c}
  9. p.cond = make(chan struct{})
  10. p.startCleanerLocked(time.Duration(c.IdleTimeout))
  11. return p
  12. }

startCleanerLocked(t Duration)函数中,有一个select...case...default代码块:

  1. // startCleanerLocked
  2. func (p *List) startCleanerLocked(d time.Duration) {
  3. if d <= 0 {
  4. // 如果设置为0,staleCleaner()将直接返回
  5. return
  6. }
  7. if d < time.Duration(p.conf.IdleTimeout) && p.cleanerCh != nil {
  8. select {
  9. case p.cleanerCh <- struct{}{}:
  10. default:
  11. }
  12. }
  13. // 只运行一次,清理过期项目。
  14. if p.cleanerCh == nil {
  15. p.cleanerCh = make(chan struct{}, 1)
  16. go p.staleCleaner()
  17. }
  18. }

这个代码块的作用是什么:

  1. select {
  2. case p.cleanerCh <- struct{}{}:
  3. default:
  4. }

看起来它似乎没有什么作用...

staleCleaner()函数中,也有一个相同的空的select...case...case,同样无法理解其作用:

  1. // staleCleaner清理过期项目的处理。
  2. func (p *List) staleCleaner() {
  3. ticker := time.NewTicker(100 * time.Millisecond)
  4. for {
  5. select {
  6. case <-ticker.C:
  7. case <-p.cleanerCh: // maxLifetime被更改或数据库被关闭。
  8. }
  9. p.mu.Lock()
  10. if p.closed || p.conf.IdleTimeout <= 0 {
  11. p.mu.Unlock()
  12. return
  13. }
  14. for i, n := 0, p.idles.Len(); i < n; i++ {
  15. e := p.idles.Back()
  16. if e == nil {
  17. // 不可能发生
  18. break
  19. }
  20. ic := e.Value.(item)
  21. if !ic.expired(time.Duration(p.conf.IdleTimeout)) {
  22. // 不需要继续。
  23. break
  24. }
  25. p.idles.Remove(e)
  26. p.release()
  27. p.mu.Unlock()
  28. ic.c.Close()
  29. p.mu.Lock()
  30. }
  31. p.mu.Unlock()
  32. }
  33. }
英文:

I'm trying to understand a pool library codes, and when instancing a pool struct, call a function named startCleanerLocked(t Duration), in this function, there's one empty select...case...default... code block, I cann't understand what is the effect for this code block.

Pool Interface is:

  1. // Pool interface.
  2. type Pool interface {
  3. Get(ctx context.Context) (io.Closer, error)
  4. Put(ctx context.Context, c io.Closer, forceClose bool) error
  5. Close() error
  6. }

List Struct implement Pool Interface,

  1. type List struct {
  2. // New is an application supplied function for creating and configuring a
  3. // item.
  4. //
  5. // The item returned from new must not be in a special state
  6. // (subscribed to pubsub channel, transaction started, ...).
  7. New func(ctx context.Context) (io.Closer, error)
  8. // mu protects fields defined below.
  9. mu sync.Mutex
  10. cond chan struct{}
  11. closed bool
  12. active int
  13. // clean stale items
  14. cleanerCh chan struct{}
  15. // Stack of item with most recently used at the front.
  16. idles list.List
  17. // Config pool configuration
  18. conf *Config
  19. }

when Create a new pool, startCleanerLocked(t Duration) function be called:

  1. // NewList creates a new pool.
  2. func NewList(c *Config) *List {
  3. // check Config
  4. if c == nil || c.Active &lt; c.Idle {
  5. panic(&quot;config nil or Idle Must &lt;= Active&quot;)
  6. }
  7. // new pool
  8. p := &amp;List{conf: c}
  9. p.cond = make(chan struct{})
  10. p.startCleanerLocked(time.Duration(c.IdleTimeout))
  11. return p
  12. }

and in startCleanerLocked(t Duration), there is a select...case...default:

  1. // startCleanerLocked
  2. func (p *List) startCleanerLocked(d time.Duration) {
  3. if d &lt;= 0 {
  4. // if set 0, staleCleaner() will return directly
  5. return
  6. }
  7. if d &lt; time.Duration(p.conf.IdleTimeout) &amp;&amp; p.cleanerCh != nil {
  8. select {
  9. case p.cleanerCh &lt;- struct{}{}:
  10. default:
  11. }
  12. }
  13. // run only one, clean stale items.
  14. if p.cleanerCh == nil {
  15. p.cleanerCh = make(chan struct{}, 1)
  16. go p.staleCleaner()
  17. }
  18. }

what's the effect for this code block:

  1. select {
  2. case p.cleanerCh &lt;- struct{}{}:
  3. default:
  4. }

Seems it's nothing to do...

and in staleCleaner(), there is a same empty select..case...case, also cannot undestand its effect:

  1. // staleCleaner clean stale items proc.
  2. func (p *List) staleCleaner() {
  3. ticker := time.NewTicker(100 * time.Millisecond)
  4. for {
  5. select {
  6. case &lt;-ticker.C:
  7. case &lt;-p.cleanerCh: // maxLifetime was changed or db was closed.
  8. }
  9. p.mu.Lock()
  10. if p.closed || p.conf.IdleTimeout &lt;= 0 {
  11. p.mu.Unlock()
  12. return
  13. }
  14. for i, n := 0, p.idles.Len(); i &lt; n; i++ {
  15. e := p.idles.Back()
  16. if e == nil {
  17. // no possible
  18. break
  19. }
  20. ic := e.Value.(item)
  21. if !ic.expired(time.Duration(p.conf.IdleTimeout)) {
  22. // not need continue.
  23. break
  24. }
  25. p.idles.Remove(e)
  26. p.release()
  27. p.mu.Unlock()
  28. ic.c.Close()
  29. p.mu.Lock()
  30. }
  31. p.mu.Unlock()
  32. }
  33. }

答案1

得分: 0

  1. select {
  2. case p.cleanerCh <- struct{}{}:
  3. default:
  4. }

这是一个非阻塞的 select 语句(因为有一个 default: 分支)。

如果 p.cleanerCh 通道的另一端有一个接收器 goroutine,也就是说有一个 goroutine 正在等待接收操作 <-p.cleanerCh,那么 case p.cleanerCh <- struct{}{} 会立即执行,从而解除接收操作 <-p.cleanerCh 的阻塞,然后 goroutine 可以继续执行后续的语句。

如果没有接收器 goroutine,那么 default: 分支会立即执行,startCleanerLocked 函数可以继续执行 select 语句后面的语句。


  1. select {
  2. case <-ticker.C:
  3. case <-p.cleanerCh: // maxLifetime was changed or db was closed.
  4. }

这是一个阻塞的 select 语句(因为没有 default: 分支)。

这个 select 语句会阻塞 for 循环,直到两个通信分支中的一个准备好接收数据。

英文:
  1. select {
  2. case p.cleanerCh &lt;- struct{}{}:
  3. default:
  4. }

This is a non-blocking select statement. (because there is a default: case)

If there is a receiver goroutine at the other end of the p.cleanerCh channel, i.e. there is a goroutine that's currently "waiting" at a receive operation i.e. &lt;-p.cleanerCh, then the case p.cleanerCh &lt;- struct{}{} is executed immediately which effectively unblocks the receive operation &lt;-p.cleanerCh and then the goroutine can proceed to execute whatever statements follow.

If there is no receiver goroutine then the default: case is immediately executed and the surrounding startCleanerLocked function can proceed to execute whatever statement follow the select statement.


  1. select {
  2. case &lt;-ticker.C:
  3. case &lt;-p.cleanerCh: // maxLifetime was changed or db was closed.
  4. }

This is a blocking select statement. (because there is no default: case)

This select statement blocks the for loop until one of the two communication cases is ready to receive.

huangapple
  • 本文由 发表于 2022年2月14日 17:14:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/71109453.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定