这是我参与「第五届青训营 」伴学笔记创作活动的第 12 天
基本概念
- 进程与线程
- 进程:程序在操作系统的一次执行过程中系统进行资源分配与调度的一个独立单位
- 线程:进程的一个执行实体,CPU调度与分派的基本单位,是比进程更小的能独立运行的基本单位。
- 进程与线程的关系:一个进程中可以创建和撤销多个线程,一个进程中的多个线程可以并发执行
- 并发与并行
- 并发:多个线程在CPU的一个核上运行,例如只用右手交替打字操控鼠标
- 并行:多个线程在CPU的多个核上运行,例如使用左手打字,右手操纵鼠标
- 协程和线程
- 协程:独立的栈空间,共享堆空间,调度控制依赖于用户自己实现
- 线程:一个线程上可以运行多个协程序
goroutine
Go 官方实现了 goroutine 这一工具,由于其具有极小的内存消耗、高效的创建与回收机制,使得 Go 在现在成为了一种极其适合高并发场景的语言。
goroutine 提倡使用通信来共享内存,而非通过共享内存来实现通信。
goroutine 默认使用全部 CPU 核心,也可以自己设置核数
goroutine 的使用
go + 函数名即可实现 goroutine,例如
func test() {
fmt.Println("test")
}
func main() {
go tset()
time.Sleep(time.Second)
}
当同时启动多个 goroutine 时可以通过使用 sync.WaitGroup
来实现同步,例如
var wg sync.WaitGroup
func test() {
fmt.Println("test")
wg.Done()
}
func main() {
wg.Add(10)
for i := 0; i < 10; i++ {
go hello(i)
}
wg.Wait()
}
以上就是 goroutine 最基础的使用了.使用 goroutine 时搭配闭包可以使我们更加灵活与轻便的写出自己想要的代码。
func main() {
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func(){
fmt.Println("test")
wg.Done()
}()
}
wg.Wait()
}
在大项目中的应用
对于大项目中的获取评论列表以及获取视频列表的接口操作,我们发现当视频列表长度较长或者评论较多时,简单地使用 for 循环无法最大化地提高 CPU 利用率,因此我们可以对于列表中的每一个视频或是每一条评论,开启一个 routine 来进行操作, 代码如下所示:
// FillVideos 更新视频作者信息
func FillVideos(userid int64, videos *[]*models.Video) (*time.Time, error) {
videosLen := len(*videos)
if videos == nil || videosLen == 0 {
return nil, errors.New("FillVideos" + errortype.PointerIsNilErr)
}
//dao := models.NewUserInfoDAO()
p := cache.NewProxyIndexMap()
latestTime := (*videos)[videosLen-1].CreatedAt
wg := sync.WaitGroup{}
wg.Add(videosLen)
// 依据列表长度开启若干进程填入关注与喜欢等信息
for i := 0; i < videosLen; i++ {
go func(i int, p *cache.ProxyCache, videos *[]*models.Video) {
var author models.UserInfo
if err := models.NewUserInfoDAO().QueryUserInfoById((*videos)[i].UserInfoId, &author); err != nil {
return
}
author.IsFollow = p.GetAFollowB(userid, author.Id)
(*videos)[i].Author = author
if userid > 0 {
(*videos)[i].IsFavorite = p.GetVideoFavor(userid, (*videos)[i].Id)
}
wg.Done()
}(i, p, videos)
}
wg.Wait()
return &latestTime, nil
}