1、协程和go关键字
协程,自己管理线程,把线程控制到一定的数量,然后构造一个规则状态机来调度任务。
package main
import (
"fmt"
"time"
)
func Hu(){
time.Sleep(2 * time.Second)
fmt.Println("after 2 second hu!!!")
}
func main(){
//开启新的协程,不会堵塞
go Hu()
fmt.Println("start hu,wait...")
//必须死循环,不然主协程退出了,程序就结束了
for{
time.Sleep(1 * time.Second)
}
}
结果显示:
start hu,wait...
after 2 second hu!!!
如果直接使用Hu()函数时,程序将会堵塞
然后使用go关键字开启一个新的协程,不在堵塞,在Hu()执行的时候,可以继续往下执行。
函数main作为程序的主协程,如果main函数结束的话,其它线程也会死掉,所以必须使用死循环来避免主协程终止。
2、信道chan
在两个协程之间进行通信,提供chan信道
package main
import (
"fmt"
"time"
)
func Hu(ch chan int){
//使用睡眠模仿一些耗时
time.Sleep(2 * time.Second)
fmt.Println("after 2 second hu!!!")
//执行语句后,通知主线程已经完成操作
ch <- 1000
}
func main(){
//新建一个没有缓冲的信道
ch := make(chan int)
//将信道传入函数,开启协程
go Hu(ch)
fmt.Println("start hu,wait...")
//确保有消息的来到
v := <-ch
fmt.Println("receive:",v)
}
结果显示:
start hu,wait...
after 2 second hu!!!
receive: 1000
我们执行协程后,因为函数里面会睡眠两秒钟,所以两秒钟之后信道才会收到消息
在没有收到消息之前 v := <-ch 会堵塞
直到协程 go Hu(ch) 完成,那么消息收到
程序结束。
3、锁实现与并发安全
多个协程可能对同一个变量做修改操作,可能不符合预期,比如转账:
因为转账是并发的,减钱操作会读取结构体 Money 里面的 amount,同时操作时可能读到同一个值。
我们需要实现并发安全,同一时间只能允许一个协程修改金额,我们需要加锁,如下:
package main
import (
"fmt"
"sync"
"time"
)
type Money struct{
lock sync.Mutex //锁
amount int64
}
func (m *Money) Add(i int64) {
//加锁
m.lock.Lock()
//在函数执行结束后执行
defer m.lock.Unlock()
m.amount = m.amount + i
}
func (m *Money) Minute(i int64) {
//加锁
m.lock.Lock()
//在函数执行结束后执行
defer m.lock.Unlock()
if m.amount >= i{
m.amount = m.amount - i
}
}
func (m *Money) Get() int64 {
return m.amount
}
func main(){
m := new(Money)
m.Add(10000)
for i := 0;i < 1000;i++{
go func(){
time.Sleep(500 * time.Millisecond)
m.Minute(5)
}()
}
time.Sleep(20 * time.Second)
fmt.Println(m.Get())
}
5000