## 一、概念
* go中channel实现了同步,确保并发安全,同时也提供了锁的操作方式
* go中sync包提供了锁相关的支持
* Mutex:以加锁方式解决并发安全问题
* sync.WaitGroup:用来等待一组子协程的结束,需要设置等待的个数,每个子协程结束后要调用Done(),最后在主协程中Wait()即可。
Add():添加计数
Done():操作结束时调用,计数减1
Wait():等待所有操作结束
## 二、加锁与不加锁的区别
#### 不加锁
~~~
package main
import (
"time"
"fmt"
)
//账户
type Account struct {
money int
}
//模拟安全校验的
func (a *Account) Check() {
time.Sleep(time.Second)
}
//设置余额
func (a *Account) SetAccount(n int) {
a.money += n
}
//查询余额
func (a *Account) GetAccount() int {
return a.money
}
//买东西
func (a *Account) Buy(n int) {
if a.money > n {
a.Check()
a.money -= n
}
}
func main() {
var account Account
account.SetAccount(100)
//开2个协程买东西
go account.Buy(60)
go account.Buy(50)
time.Sleep(2 * time.Second)
fmt.Println(account.GetAccount())
}
~~~
运行结果
-10
#### 加锁
~~~
package main
import (
"time"
"fmt"
"sync"
)
//账户
type Account struct {
money int
flag sync.Mutex //设置锁
}
//模拟安全校验的
func (a *Account) Check() {
time.Sleep(time.Second)
}
//设置余额
func (a *Account) SetAccount(n int) {
a.money += n
}
//查询余额
func (a *Account) GetAccount() int {
return a.money
}
//买东西
func (a *Account) Buy(n int) {
//加锁
a.flag.Lock()
if a.money > n {
a.Check()
a.money -= n
}
//解锁
a.flag.Unlock()
}
func main() {
var account Account
account.SetAccount(100)
//开2个协程买东西
go account.Buy(60)
go account.Buy(50)
time.Sleep(2 * time.Second)
fmt.Println(account.GetAccount())
}
~~~
执行结果:
50
## 三、如果主协程中没有内容子协程就不会去执行
#### 主协程没有内容
~~~
package main
import (
"fmt"
)
func main() {
go func() {
fmt.Println("子协程1")
}()
go func() {
fmt.Println("子协程2")
}()
}
~~~
运行结果:
#### 解决原理
~~~
package main
import (
"fmt"
)
func main() {
m1 := make(chan int)
// 子协程活动个数
count :=2
go func() {
fmt.Println("子协程1")
// 协程1结束,发出信号
m1 <- 1
}()
go func() {
fmt.Println("子协程2")
// 协程2结束,发出信号
m1 <- 1
}()
for range m1{
//读一个数据 count-1
count --
if count ==0{
close(m1)
}
}
}
~~~
执行结果:
子协程2运行
子协程1运行
#### 使用 sync.WaitGroup 解决
~~~
package main
import (
"sync"
"fmt"
)
func main() {
//声明等待组
var wg sync.WaitGroup
wg.Add(2)
go func() {
fmt.Println("子协程1运行")
wg.Done()
}()
go func() {
fmt.Println("子协程2运行")
wg.Done()
}()
wg.Wait()
}
~~~
执行结果:
子协程2运行
子协程1运行
- 一、数组
- 二、切片
- 三、copy
- 四、MAP
- 五、结构体
- 六、结构体参数
- 七、面向”对象“
- 1、匿名字段
- 2、方法
- 3、包和封装
- 4、接口
- 5、异常处理
- 八、Json
- 九、文件操作
- 1、写文件
- 2、读取文件内容
- 3、拷贝文件
- 十、反射
- 1、查看类型,字段和方法
- 2、查看匿名字段
- 3、修改基本类型的值
- 4、修改结构体的值
- 5、调用方法
- 十一、并发编程
- 1、并行和并发
- 2、协程
- 3、runtime包
- 5、channel的使用
- 6、close
- 7、定时器
- 8、select
- 9、协程同步锁
- 十二、socket编程
- 十三、Http编程
- 十四、并发爬虫和数据处理
- 1、简易爬虫实例
- 2、并发爬取图片
- 3、读文件中的数据
- 4、数据清洗
- 其他
- 1、推荐文章