-
并发
Golang使用
sync.WaitGroup
来实现并发任务的同步:
sync.WaitGroup
内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了N 个并发任务时,就将计数器值增加N。每个任务完成时通过调用Done()方法将计数器减1。通过调用Wait()来等待并发任务执行完,当计数器值为0时,表示所有并发任务已经完成。
方法 | 注释 |
---|---|
| 计数器+delta |
| 计数器-1 |
| 阻塞直到计数器变为0 |
var wg sync.WaitGroup
wg.Add(1) // 启动一个goroutine就登记+1
go doDir() //自定义函数处理业务逻辑
wg.Wait() // 等待所有登记的goroutine都结束
func doDir(){
defer wg.Done() //goroutine结束就登记-1
}
goroutine
是并发执行的,而goroutine
的调度是随机的。
goroutine中最主要的是三个实体为GMP:
G
代表一个goroutine对象,每次go调用的时候,都会创建一个G对象,它包括栈、指令指针以及对于调用goroutines很重要的其它信息,比如阻塞它的任何channelP
代表一个处理器,管理着一组goroutine队列,每一个运行的M都必须绑定一个P,P里面会存储当前goroutine运行的上下文环境(函数指针,堆栈地址及地址边界),P会对自己管理的goroutine队列做一些调度(比如把占用CPU时间较长的goroutine暂停、运行后续的goroutine),当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他P的队列里抢任务。P的个数就是GOMAXPROCS(最大256),启动时固定的,一般不修改,每一个P保存着本地G任务队列,也有一个全局G任务队列。M(machine)
是Go运行时(runtime)对操作系统内核线程的虚拟,代表一个线程,每次创建一个M的时候,都会有一个底层线程创建, M与内核线程一般是一一映射的关系, 一个groutine最终是要放到M上执行的。
*注意 sync.WaitGroup
是一个结构体,传递的时候要传递指针。
-
事务
func execTransaction(db *sql.DB) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer func() {
if err != nil {
tx.Rollback()
return
}
}()
err = execSql(tx) //sql执行处理
if err != nil {
return err
}
return tx.Commit()
}
func execSql(tx *sql.Tx){
var sqlCmd = fmt.Sprintf("insert into table values(...)")
tx.Exec(sql)
}
注意 tx.Exec(sql) 而不是db.Exec(sql),否则就是两个连接,事务不起作用。