Golang并发编程实战 《一》(一个完整的多协程并发控制的例子)

163 阅读1分钟
  • 此函数例子,完整展示了使用 协程 加 waitgroup, 锁,以及通道,和 通道close广播机制,完成并发修改数据库多行返回值的操作,是非常标准的golang并发编程范式
  • 后边我会详细的跟大家介绍 协程 , waitgroup, 锁,以及通道,和 通道close广播机制的原理

 业务处理函数, 获取用户列表
func ListUser(username string, offset, limit int) ([]*model.UserInfo, uint, error) {
   infos := make([]*model.UserInfo, 0)
   users, count, err := model.ListUser(username, offset, limit)
   if err != nil {
      return nil, count, err
   }

   ids := []uint{}
   for _, user := range users {
      ids = append(ids, user.ID)
   }

   wg := sync.WaitGroup{}
   userList := model.UserList{
      Lock:  new(sync.Mutex),
      IdMap: make(map[uint]*model.UserInfo, len(users)),
   }

   errChan := make(chan error, 1)
   声明一个用来做关闭通知的通道
   finished := make(chan bool, 1)

   // 并行转换
   for _, u := range users {
     每个协程加入waitgroup等待
      wg.Add(1)
      go func(u *model.UserModel) {
        每个协程 设置done操作
         defer wg.Done()

         shortID, err := util.GenShortID()
         if err != nil {
            errChan <- err
            return
         }

         更新数据时加锁, 保持一致性
         userList.Lock.Lock()
         defer userList.Lock.Unlock()

         userList.IdMap[u.ID] = &model.UserInfo{
            ID:        u.ID,
            Username:  u.Username,
            SayHello:  fmt.Sprintf("Hello %s", shortID),
            Password:  u.Password,
            CreatedAt: util.TimeToStr(&u.CreatedAt),
            UpdatedAt: util.TimeToStr(&u.UpdatedAt),
            DeletedAt: util.TimeToStr(u.DeletedAt),
         }
      }(u)
   }

   go func() {
      wg.Wait()
      等待所有协程完成之后,关闭通道,close有广播机制
      close(finished)
   }()

   // 等待完成
   select {
  如果监听到finished通道的关闭信号则继续执行
   case <-finished:
   case err := <-errChan:
      return nil, count, err
   }

   for _, id := range ids {
      infos = append(infos, userList.IdMap[id])
   }

   return infos, count, nil
}