go select + channel 常见用法

for-select

for-select 是一个很常见的用法,select 语句可以从多个可读的 channel 中随机选取一个执行。
向 channel 发送迭代变量:

ch := make(chan int, 2)
for _, s := range []int{1, 2} {
    select {
    case ch <- s:
    }
}

循环等待停止:

// 第一种
for {
  select {
  case <- done:
    return
  default:
    // 进行非抢占式任务
  }
}
// 第二种
for {
  select {
  case <- done:
    return
  default:
  }
  // 进行非抢占式任务
}

第一种是指,当我们输入 select 语句时,如果完成的 channel 尚未关闭,我们将执行 default 语句;第二种是指,如果已经完成的 channel 未关闭,我们将退出 select 语句并继续执行 for 循环的其余部分。

超时控制

等待 3 s 后,如果 s.stopc 还没有读出数据或者被关闭,就直接结束。

select {
    case <-time.After(3 * time.Second):
    case <-s.stopc:
        return false
}

done channel

防止 goroutine 泄露。用 done channel 在父子 goroutine 之间建立一个 “信号通道”,父 goroutine 可以将该 channel 传递给子 goroutine ,然后在想要取消子 goroutine 的时候关闭该 channel。

func main() {
    doneChan := make(chan interface{})
    go func(done <-chan interface{}) {
       for {
          select {
          case <-done:
            return
          default:
          }
        }
    }(doneChan)
    // 父 goroutine 关闭子 goroutine
    close(doneChan)
}

确保 goroutine 不泄露的方法,就是规定一个约定:如果 goroutine 负责创建 goroutine,它也负责确保它可以停止 goroutine。

定时执行某个任务

//每隔 1 秒种,执行一次定时任务。
func worker() {
    ticker := time.Tick(1 * time.Second)
    for {
        select {
        case <- ticker:
            // 执行定时任务
        }
    }
}

解耦生产方和消费方

func main() {
    taskCh := make(chan int, 10)
    go worker(taskCh)
    // 塞任务
    for i := 0; i < 10; i++ {
        taskCh <- i
    }
    // 等待 1 小时 
    select {
    case <-time.After(time.Hour):
    }
}

func worker(taskCh <-chan int) {
    const N = 5
    // 启动 5 个工作协程
    for i := 0; i < N; i++ {
        go func(id int) {
            for {
                task := <- taskCh
                time.Sleep(time.Second)
            }
        }(i)
    }
}

5 个工作协程在不断地从工作队列里取任务,生产方只管往 channel 发送任务即可,解耦生产方和消费方。

控制并发数

在并发数较高时,因为任务执行过程依赖其他模块的一些资源
对请求的速率有限制,这时就可以通过 channel 来控制并发数。

var limit = make(chan int, 3)
func main() {
    // …………
    for _, w := range work {
        go func() {
            limit <- 1
            proc()
            <-limit
        }()
    }
    // …………
}

构建一个缓冲型的 channel,容量为 3。接着遍历任务列表,每个任务启动一个 goroutine 去完成。真正执行任务,访问第三方模块动作在 proc() 中完成,在执行 proc() 之前,先要从 limit 中拿“许可证”,拿到许可证之后,才能执行 proc(),并且在执行完任务,要将“许可证”归还。这样就可以控制同时运行的 goroutine 数。

你可能感兴趣的:(go select + channel 常见用法)