一、协程的创建
Go 语言支持并发,只需要通过 go 关键字来开启 goroutine(协程) 即可。
goroutine(协程) 是轻量级线程,goroutine(协程) 的调度是由 Golang 运行时进行管理的。
goroutine 语法格式(创建协程):
go 函数名( 参数列表 )
示例代码如下:
1 package main 2 import ( 3 "fmt" 4 "time" 5 ) 6 func rest(msg string) { 7 for i := 0; i < 4; i++ { 8 fmt.Println(msg) 9 time.Sleep(100 * time.Millisecond) 10 } 11 } 12 func main() { 13 // 开启一个goroutine 协程运行 14 go rest(" go rest ") 15 // main主线程运行 16 rest(" main rest ") 17 }
执行结果如下:
二、协程间的同步
2.1、WaitGroup
WaitGroup,和Java中的CountDownLatch实现原理相似,WaitGroup持有当前正在执行的协程数量,当某个协程执行完后,WaitGroup持有正在执行的协程数量减1,当WaitGroup中协程正执行的数量为0,wait()方法便不再阻塞。
示例代码如下:
1 package main 2 3 import ( 4 "fmt" 5 "sync" 6 ) 7 8 var wg sync.WaitGroup 9 10 func routine01(num int) { 11 // goroutine结束登记-1 12 defer wg.Done() 13 fmt.Printf("goroutine runing, %v\n", num) 14 } 15 16 func main() { 17 for i := 0; i < 8; i++ { 18 // 启动一个goroutine就加1 19 wg.Add(1) 20 go routine01(i) 21 } 22 // 阻塞,等待所有登记的goroutine结束 23 wg.Wait() 24 }
执行结果如下:
2.2、Mutex互斥同步
1 package main 2 import ( 3 "fmt" 4 "sync" 5 "time" 6 ) 7 var m = 100 8 var n = 100 9 var lock sync.Mutex 10 var wagp sync.WaitGroup 11 // 增量函数 12 func add() { 13 defer wagp.Done() 14 n += 1 15 time.Sleep(time.Millisecond * 8) 16 } 17 // 减量函数 18 func sub() { 19 time.Sleep(time.Millisecond * 3) 20 defer wagp.Done() 21 n -= 1 22 } 23 // 未用mutex同步锁 24 func nomutex() { 25 for i := 0; i < 100; i++ { 26 go add() 27 wagp.Add(1) 28 go sub() 29 wagp.Add(1) 30 } 31 32 wagp.Wait() 33 } 34 // 用mutex同步锁 35 func addMetux() { 36 defer wagp.Done() 37 lock.Lock() 38 m += 1 39 time.Sleep(time.Millisecond * 10) 40 lock.Unlock() 41 } 42 // 用mutex同步锁 43 func subMetux() { 44 defer wagp.Done() 45 lock.Lock() 46 time.Sleep(time.Millisecond * 3) 47 m -= 1 48 lock.Unlock() 49 } 50 // 使用mutex同步锁 51 func mutex01() { 52 for i := 0; i < 100; i++ { 53 go addMetux() 54 wagp.Add(1) 55 go subMetux() 56 wagp.Add(1) 57 } 58 59 wagp.Wait() 60 } 61 func main() { 62 nomutex() 63 mutex01() 64 fmt.Printf("end n: %v\n", n) 65 fmt.Printf("end m: %v\n", m) 66 }
执行结果如下:
mutex,和Java中的Lock实现原理相似。当执行lock()方法时,基于 CAS 操作,将Mutex的 state 设置为Metux为加锁状态,同时设置当前线程持有锁资源;执行Unlock()方法时,对state的值做调整,释放锁资源,唤醒等待线程。
三、协程管理相关包
协程管理在api-runtime的相关包下。
1、runtime.Gosched()
让出CPU时间片,重新等待安排任务。
1 package main 2 3 import ( 4 "fmt" 5 "runtime" 6 ) 7 8 func show(msg string) { 9 for i := 0; i < 3; i++ { 10 fmt.Printf("msg: %v\n", msg) 11 } 12 } 13 14 func main() { 15 go show("php") 16 // 主协程 17 for i := 0; i < 2; i++ { 18 // 让出cpu给子协程 19 runtime.Gosched() // 若此处注释,则子协程可能无法执行 20 fmt.Printf("%v\n", "golang") 21 } 22 }
执行结果如下:
2、runtime.Goexit()
退出子协程。
示例如下:
1 package main 2 3 import ( 4 "fmt" 5 "runtime" 6 ) 7 8 func show01(msg string) { 9 for i := 0; i < 8; i++ { 10 if i == 3 { 11 // 退出子协程 12 runtime.Goexit() 13 } 14 fmt.Printf("msg: %v\n", msg) 15 } 16 } 17 18 func main() { 19 go show01("C") 20 for i := 0; i < 1; i++ { 21 runtime.Gosched() 22 fmt.Printf("%v\n", "golang") 23 } 24 }
执行结果如下:
四、select
select是Go中并发编程的控制语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当channel为非阻塞状态(可读写),会触发select中的case。
1、select的语法结构
select { case 读: // do something case 写: // do something default: // do something }
若多个case都可运行,select随机选择一个执行,其他不会执行。没有可运行的case语句,有default语句,会执行default。
2、示例
1 package main 2 3 import "fmt" 4 5 // 创建通道 6 var chint = make(chan int, 5) 7 var chstr = make(chan string, 5) 8 9 func main() { 10 go func() { 11 // 关闭通道 12 defer close(chint) 13 defer close(chstr) 14 // 往通道里写数据 15 chint <- 10 16 chstr <- "chstr" 17 }() 18 19 for i := 0; i < 10; i++ { 20 // 监听case语句中channel的读写操作,当channel为非阻塞状态(可读写),会触发select中的case 21 select { 22 case i := <-chint: 23 fmt.Printf("i: %v\n", i) 24 case s := <-chstr: 25 fmt.Printf("s: %v\n", s) 26 default: 27 fmt.Printf("default...\n") 28 } 29 } 30 }
执行结果如下:
3、select注意事项
没有可运行的case语句,没有default语句,select会阻塞直到某个case通信可运行;
select中的case语句必须是一个channel操作;
select的default总是可运行的。
五、定时器Timer
定时器Timer只执行一个。
1、timer的创建
timer := time.NewTimer(time.Second) timer.C // 阻塞,直到指定时间到了
示例如下:
1 package main 2 import ( 3 "fmt" 4 "sync" 5 "time" 6 ) 7 var wait sync.WaitGroup 8 // 使用定时器 9 func time01() { 10 defer wait.Done() 11 fmt.Printf("before: %v\n", time.Now()) 12 timer := time.NewTimer(time.Second * 2) 13 <-timer.C // 阻塞,直到指定时间到达 14 fmt.Printf("after: %v\n", time.Now()) 15 } 16 func main() { 17 wait.Add(1) 18 go time01() 19 // 同步等待协程执行完成 20 wait.Wait() 21 }
执行结果如下:
2、time.After
time.After,和Java中的Sleep功能相似,阻塞指定时间再运行。
示例如下:
1 package main 2 import ( 3 "fmt" 4 "sync" 5 "time" 6 ) 7 var wait sync.WaitGroup 8 9 // 使用定时器 10 func time02() { 11 defer wait.Done() 12 fmt.Printf("before: %v\n", time.Now()) 13 // 阻塞2s 14 <-time.After(time.Second * 2) 15 fmt.Printf("after: %v\n", time.Now()) 16 } 17 18 func main() { 19 wait.Add(1) 20 go time02() 21 // 同步等待协程执行完成 22 wait.Wait() 23 }
执行结果如下:
3、timer.stop
停止定时器事件。
示例如下:
1 package main 2 import ( 3 "fmt" 4 "time" 5 ) 6 7 func main() { 8 // 创建定时器 9 timer := time.NewTimer(time.Second * 2) 10 // 协程,匿名函数 11 go func() { 12 <-timer.C 13 fmt.Println("timer func...") 14 }() 15 16 // 停止定时器,阻止timer事件发生 17 stopFlag := timer.Stop() 18 if stopFlag { 19 fmt.Println("timer stoped...") 20 } 21 }
执行结果如下:
4、timer.Reset
重置定时器。
示例如下:
1 package main 2 import ( 3 "fmt" 4 "time" 5 ) 6 7 func main() { 8 // 重置定时器 reset 9 fmt.Printf("before: %v\n", time.Now()) 10 timer := time.NewTimer(time.Second * 2) 11 timer.Reset(time.Second * 1) 12 <-timer.C 13 fmt.Printf("after: %v\n", time.Now()) 14 }
执行结果如下:
六、周期执行器Ticker
与timer只执行一次相比,Ticker可周期执行。
示例如下:
1 package main 2 import ( 3 "fmt" 4 "time" 5 ) 6 func main() { 7 // 创建周期执行器 8 ticker := time.NewTicker(time.Second * 2) 9 10 // 周期执行 11 for _ = range ticker.C { 12 fmt.Printf("time: %v\n", time.Now()) 13 } 14 }
执行结果如下:
1 package main 2 import ( 3 "fmt" 4 "time" 5 ) 6 7 func ticker() { 8 // 创建一个无缓冲的整型channel 9 chint := make(chan int) 10 // 关闭通道 11 defer close(chint) 12 // 创建定时器 13 ticker := time.NewTicker(time.Second * 2) 14 // 创建一个协程,利用周期定时器每2s向通道中写入数据 15 go func() { 16 for _ = range ticker.C { 17 select { 18 case chint <- 2: 19 case chint <- 4: 20 case chint <- 6: 21 } 22 } 23 }() 24 sum := 0 25 // 遍历通道,若通道无数据,会阻塞 26 for v := range chint { 27 fmt.Printf("receive: %v\n", v) 28 sum += v 29 if sum > 20 { 30 fmt.Printf("sum: %v\n", sum) 31 break 32 } 33 } 34 } 35 36 func main() { 37 ticker() 38 }
执行结果如下:
七、atomic
atomic的原子操作可以保证任一时刻只有一个goroutine协程对变量做修改。
1、加减
1 package main 2 import ( 3 "fmt" 4 "sync" 5 "sync/atomic" 6 ) 7 var i int32 = 100 8 var waitg sync.WaitGroup 9 // 原子加操作 10 func atomicAdd() { 11 atomic.AddInt32(&i, 1) 12 waitg.Done() 13 } 14 // 原子减操作 15 func atomicSub() { 16 atomic.AddInt32(&i, -1) 17 waitg.Done() 18 } 19 func main() { 20 for i := 0; i < 100; i++ { 21 // 子线程 22 waitg.Add(1) 23 go atomicAdd() 24 // 子线程 25 waitg.Add(1) 26 go atomicSub() 27 } 28 waitg.Wait() 29 fmt.Printf("i = %v\n", i) 30 }
执行结果如下:
2、载入 -> 读取操作、存储 -> 写入操作
1 package main 2 import ( 3 "fmt" 4 "sync/atomic" 5 ) 6 // 载入与存储 7 func loadAndStore() { 8 var i int64 = 64 9 // 载入 -> 读取,原子操作 10 atomic.LoadInt64(&i) 11 fmt.Printf("i: %v\n", i) 12 13 // 存储 -> 写入,原子操作 14 atomic.StoreInt64(&i, 128) 15 fmt.Printf("i: %v\n", i) 16 } 17 func main() { 18 loadAndStore() 19 }
执行结果如下:
3、CAS -> 比较并交换
进行交换前变量的值未被修改,与参数old记录的值一致,满足此前提下才会进行交换。
1 package main 2 import ( 3 "fmt" 4 "sync/atomic" 5 ) 6 // 比较和交换 7 func cas() { 8 var i int64 = 256 9 // 旧的值与变量i的值相同,则交换 10 result := atomic.CompareAndSwapInt64(&i, 256, 64) 11 if result { 12 fmt.Println("cas success") 13 } else { 14 fmt.Println("cas fail") 15 } 16 fmt.Printf("i: %v\n", i) 17 18 // 旧的值与变量i的值不同,则不交换 19 result01 := atomic.CompareAndSwapInt64(&i, 256, 8) 20 if result01 { 21 fmt.Println("cas success") 22 } else { 23 fmt.Println("cas fail") 24 } 25 fmt.Printf("i: %v\n", i) 26 } 27 func main() { 28 cas() 29 }
执行结果如下:
标签:Printf,fmt,编程,并发,func,time,Go,main,go From: https://www.cnblogs.com/RunningSnails/p/17375894.html