协程,也叫gorountine
go 语言诞生比较晚,web2.0开发逐渐主流,高并发需求大
go 一开始就没有打算让我们去实例化一个线程,而是使用协程
多线程 - 每个线程占用的内存比较多 而且系统切换开销比较大
轻量化线程 -> 协程,是用户级别的
python中有两种编程模式 1.多线程和多进程进行并发编程 2.使用协程进行并发编程
1.goruntine
func p(){
fmt.Println("bobby")
}
func main(){
go p()
}
上述代码就是简单的协程
执行结果:什么都没有
原因:主协程先关闭,主死从随
func p(){
fmt.Println("bobby")
}
func main(){ //主协程
//主死从随
go p()
time.Sleep(time.Second*2)
}
打印结果:bobby
也可以使用匿名函数
func main(){ //主协程
//主死从随
for i:=0;i < 5;i++{
go func(){
fmt.Println(i)
}
}
time.Sleep(time.Second*2)
}
打印结果:5 5 5 5 5
func main(){ //主协程
//主死从随
for i:=0;i < 5;i++{
//闭包
go func(){
fmt.Println(i)
time.Sleep(time.Second)
}
}
time.Sleep(time.Second*2)
}
打印结果:1 2 3 4 5
一直用time.Sleep去维持主协程肯定不是个事,因此我们来使用WaitGroup:
2.WaitGroup
WaitGroup通了三个很有用的函数:
Add()
Done()
Wait()
var wg sync.WaitGroup
func f(n int){
defer wg.Done()
fmt.Println(n)
}
func main(){
for i := 0; i < 5; i++{
wg.Add(1)
go f(i)
}
wg.Wait()
}
3.互斥锁
var total int
var wg sync.WaitGroup
func add(){
defer wg.Done()
for i := 0; i < 5; i++{
total = total+1
}
}
func sub(){
defer wg.Done()
for i := 0; i < 5; i++{
total = total-1
}
}
func main(){
wg.Add(2)
go add()
go sub()
wg.Wait()
fmt.Println(total)
}
理论结果:0
实际结果:不是0,随机-5~5,每次结果不一样
total = total+1
这个代码:
1.从total取出值
2.将total+1
3.将total+1的计算结果放回total中
var total int
var wg sync.WaitGroup
var lock sync.Mutex
func add(){
defer wg.Done()
for i := 0; i < 5; i++{
lock.Lock()
total = total+1
lock.Unlock
}
}
func sub(){
defer wg.Done()
for i := 0; i < 5; i++{
lock.Lock()
total = total-1
lock.Unlock
}
}
func main(){
wg.Add(2)
go add()
go sub()
wg.Wait()
fmt.Println(total)
}
运行结果:0
4. 读写锁
互斥锁性能太差
绝大多数web系统都是读多写少
var total int
var wg sync.WaitGroup
var rwlock sync.RWMutex
func read(){
defer wg.Done()
rwlock.RLock()
fmt.Println("开始读取数据")
time.Sleep(time.Second*10)
fmt.Println("读取成功")
rwLock.RUlock()
}
func write(){
defer wg.Done()
rwlock.WLock()
fmt.Println("开始修改数据")
time.Sleep(time.Second*10)
fmt.Println("修改成功")
rwLock.WUlock()
}
func main(){
wg.Add(1)
for i := 0; i < 5; i++{
wg.Add(1)
go read()
}
go write()
wg.Wait()
fmt.Println(total)
}
读取之间互不影响,写锁进来都停止
5. channel
channel提供了一种通信机制,类似java的消息队列
关键字chan
func main(){
//1.定义一个channel
var msg chan int
//2.初始化这个channel,两种方式
msg = make(chan int) //第一种初始化方式;无缓冲
//msg = make(chan int,1) //第二种初始化方式;有缓冲
//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
msg <- 1 //将1 放入channel中
data := <- msg //将箭头右边的值放到左边
fmt.Println(data)
}
运行结果:失败 deadlock
原因:无缓冲空间
func main(){
//1.定义一个channel
var msg chan int
//2.初始化这个channel,两种方式
//msg = make(chan int) //第一种初始化方式;无缓冲
msg = make(chan int,1) //第二种初始化方式;有缓冲
//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
msg <- 1 //将1 放入channel中
//msg <- 2 //再放就会报错,超出缓冲大小
data := <- msg //将箭头右边的值放到左边
fmt.Println(data)
}
运行结果:1
var wg sync.WaitGroup
func consumer (queue chan int) {
defer wg.Done()
data := <-queue
fmt.Println(data)
}
func main(){
//1.定义一个channel
var msg chan int
//2.初始化这个channel,两种方式
//msg = make(chan int) //第一种初始化方式;无缓冲
msg = make(chan int,1) //第二种初始化方式;有缓冲
//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
msg <- 1 //将1 放入channel中
wg.Add(1)
go consumer(msg)
msg <- 2
wg.Wait()
}
6. 遍历channel和关闭channel
var wg sync.WaitGroup
func consumer (queue chan int) {
defer wg.Done()
for data := range queue{
fmt.Println(data)
time.Sleep(time.Second)
}
}
func main(){
//1.定义一个channel
var msg chan int
//2.初始化这个channel,两种方式
//msg = make(chan int) //第一种初始化方式;无缓冲
msg = make(chan int,1) //第二种初始化方式;有缓冲
//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
msg <- 1 //将1 放入channel中
wg.Add(1)
go consumer(msg)
msg <- 2
//关闭channel 1.已经关闭的channel不能再发送数据了
// 2.已经关闭的channel消费者可以继续取出数据
close(msg)
wg.Wait()
}
结果:
1
2
var wg sync.WaitGroup
func consumer (queue chan int) {
defer wg.Done()
for {
data := <-queue
fmt.Println(data)
time.Sleep(time.Second)
}
}
func main(){
//1.定义一个channel
var msg chan int
//2.初始化这个channel,两种方式
//msg = make(chan int) //第一种初始化方式;无缓冲
msg = make(chan int,1) //第二种初始化方式;有缓冲
//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
msg <- 1 //将1 放入channel中
wg.Add(1)
go consumer(msg)
msg <- 2
//关闭channel 1.已经关闭的channel不能再发送数据了
// 2.已经关闭的channel消费者可以继续取出数据
close(msg)
wg.Wait()
}
结果:
1
2
0
0
0
0
…
chan取出的值有两个返回值,第二个是ok
var wg sync.WaitGroup
func consumer (queue chan int) {
defer wg.Done()
for {
data,ok := <-queue
fmt.Println(data,ok)
time.Sleep(time.Second)
}
}
func main(){
//1.定义一个channel
var msg chan int
//2.初始化这个channel,两种方式
//msg = make(chan int) //第一种初始化方式;无缓冲
msg = make(chan int,1) //第二种初始化方式;有缓冲
//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
msg <- 1 //将1 放入channel中
wg.Add(1)
go consumer(msg)
msg <- 2
//关闭channel 1.已经关闭的channel不能再发送数据了
// 2.已经关闭的channel消费者可以继续取出数据
close(msg)
wg.Wait()
}
结果:
1 true
2 true
0 false
0 false
0 false
0 false
…
使用ok判读break时机
var wg sync.WaitGroup
func consumer (queue chan int) {
defer wg.Done()
for {
data,ok := <-queue
if !ok{
break
}
fmt.Println(data)
time.Sleep(time.Second)
}
}
func main(){
//1.定义一个channel
var msg chan int
//2.初始化这个channel,两种方式
//msg = make(chan int) //第一种初始化方式;无缓冲
msg = make(chan int,1) //第二种初始化方式;有缓冲
//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
msg <- 1 //将1 放入channel中
wg.Add(1)
go consumer(msg)
msg <- 2
//关闭channel 1.已经关闭的channel不能再发送数据了
// 2.已经关闭的channel消费者可以继续取出数据
close(msg)
wg.Wait()
}
结果:
1
2
7.双向和单向channel
//无缓冲
var wg sync.WaitGroup
func consumer (queue chan int) {
defer wg.Done()
for {
data,ok := <-queue
if !ok{
break
}
fmt.Println(data)
time.Sleep(time.Second)
}
}
func main(){
//1.定义一个channel
var msg chan int
//2.初始化这个channel,两种方式
msg = make(chan int) //第一种初始化方式;无缓冲
//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
wg.Add(1)
go consumer(msg)//无缓冲,先启动消费
msg <- 1 //将1 放入channel中
//msg <- 2
//关闭channel 1.已经关闭的channel不能再发送数据了
// 2.已经关闭的channel消费者可以继续取出数据
close(msg)
wg.Wait()
}
//单、双向
var wg sync.WaitGroup
func consumer (queue <-chan int) { //只能取值。单向
defer wg.Done()
for {
data,ok := <-queue
if !ok{
break
}
fmt.Println(data)
time.Sleep(time.Second)=
}
}
func main(){
var msg chan int //双向
//var msg chan<- int //单向
msg = make(chan int)
wg.Add(1)
go consumer(msg)//普通的channel可以直接转换成单向的
msg <- 1 //将1 放入channel中
close(msg)
wg.Wait()
}
var wg sync.WaitGroup
func consumer (queue <-chan int) { //只能取值。单向
defer wg.Done()
for {
data,ok := <-queue
if !ok{
break
}
fmt.Println(data)
time.Sleep(time.Second)=
}
}
func stock(queue chan<- int{
defer wg.Done
for{
queue <- 1
time.Sleep(time.Second)
}
}
func main(){
var msg chan int //双向
//var msg chan<- int //单向
msg = make(chan int)
wg.Add(1)
go consumer(msg)//普通的channel可以直接转换成单向的
msg <- 1 //将1 放入channel中
close(msg)
wg.Wait()
}
deadlock出现场景
1.无缓冲直接放值,且没有一个goruntine去消费
2.channel是多个goruntine之间线程安全:1.获取锁,2.等到消费者消费,3.释放锁