go并发编程笔记2
并发编程之channel的遍历
//for循环加if判断
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i
}
close(c)
}()
for {
if data, ok := <-c; ok {
fmt.Printf("data: %v\n", data)
} else {
break
}
}
}
//for range
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i
}
close(c)
}()
for v := range c {
fmt.Printf("v: %v\n", v)
}
}
注意: 如果通道关闭,读多写少,没有了就是默认值,例如,int是0,如果没有关闭就会死锁。
并发编程之select
1.select是Go中的一个控制结构,类似switch语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当case中channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。
select中的case语句必须是一个channel操作
select中的default子句总是可运行的。
2.如果有多个case都可以运行,select会随机公平地选出一个执行,其他不会执行。
3.如果没有可运行的case语句,且有default语句,那么就会执行default的动作
4.如果没有可运行的case语句,且没有default语句,select将阻塞,直到某个case通信可以运行。
package main
import(
"fmt"
"time"
)
var chanInt = make(chan int)
var chanStr = make(chan string)
func main(){
go func(){
chanInt <- 100
chanString <- "hello"
close(chanInt)
close(chanStr)
}()
for{
select{
case r := <-chanInt:
fmt.Printf("chanInt:%v\n",r)
case r := <-chanStr:
fmt.Printf("chanStr:%v\n",r)
default:
fmt.Println("default...")
}
time.Sleep(time.Second)
}
}
并发编程之timer
package main
import (
"fmt"
"time"
)
func main() {
timer1 := time.NewTimer(time.Second * 2)//创建方法
t1 := time.Now()
fmt.Printf("t1:%v\n", t1)
t2 := <-timer1.C//相当于等待两秒
fmt.Printf("t2:%v\n", t2)
//如果只是想单纯的等待的话,可以使用 time.Sleep 来实现
timer2 := time.NewTimer(time.Second * 2)
<-timer2.C
fmt.Println("2s后")
time.Sleep(time.Second * 2)
fmt.Println("再一次2s后")
<-time.After(time.Second * 2) //time.After函数的返回值是chan Time
fmt.Println("再再一次2s后")
timer3 := time.NewTimer(time.Second)
go func() {
<-timer3.C
fmt.Println("Timer 3 expired")
}()
stop := timer3.Stop() //停止定时器
////阻止timer事件发生,当该函数执行后,timer计时器停止,相应的事件不再执行
if stop {
fmt.Println("Timer 3 stopped")
}
fmt.Println("before")
timer4 := time.NewTimer(time.Second * 5) //原来设置5s
timer4.Reset(time.Second * 1) //重新设置时间,即修改NewTimer的时间
<-timer4.C
fmt.Println("after")
}
<-time1.c
并发编程之Tricker
timer只执行一次,tricker可以周期性的执行。
实例
packeage main
import(
"fmt"
"time"
)
func main(){
tricker :=time.NewTicker(time.Second)
counter := 1
for _ = range ticker.C{
fmt>println("ticker 1")
counter++
if counter >=5{
break
}
}
ticker.Stop()
}
并发编程之原子变量引入
与mutex锁的作用类似
package main
import (
"fmt"
"sync/atomic"
"time"
)
var i int32 = 100
func add() {
atomic.AddInt32(&i, 1)
}
func sub() {
atomic.AddInt32(&i, -1)
}
func main() {
for i := 0; i < 100; i++ {
go add()
go sub()
}
time.Sleep(time.Second * 3)
fmt.Printf("i: %v\n", i)
}
并发编程之原子操作详解
atomic 提供的原子操作能够确保任一时刻只有一个goroutine对变量进行操作,善用 atomic 能够避免程序中出现大量的锁操作。常见操作有:
- 增减
- 载入read
- 比较并交换cas
- 交换
- 存储write
增减
atomic包提供了如下以Add为前缀的增减操作:
- func AddInt32(addr *int32, delta int32) (new int32)
- func AddInt64(addr *int64, delta int64) (new int64)
- func AddUint32(addr *uint32, delta uint32) (new uint32)
- func AddUint64(addr *uint64, delta uint64) (new uint64)
- func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
载入
以Load为前缀,载入操作能够保证原子的读变量的值,当读取的时候,任何其他的cpu操作都无法对该变量进行读写,其实现机制收到底层硬件的支持。
- func LoadInt32(addr *int32) (val int32)
- func LoadInt64(addr *int64) (val int64)
- func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
- func LoadUint32(addr *uint32) (val uint32)
- func LoadUint64(addr *uint64) (val uint64)
- func LoadUintptr(addr *uintptr) (val uintptr)
比较并交换
该操作简称CAS
该操作在进行交换前首先确保变量的值未被更改,即仍然保持参数old所记录的值,满足才进行交换操作。cas类似操作数据库时常见的乐观锁机制。
- func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
- func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
- func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
- func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
- func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
- func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
交换
前缀为Swap
- func SwapInt32(addr *int32, new int32) (old int32)
- func SwapInt64(addr *int64, new int64) (old int64)
- func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
- func SwapUint32(addr *uint32, new uint32) (old uint32)
- func SwapUint64(addr *uint64, new uint64) (old uint64)
- func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
相比CAS更暴力直接,不管变量旧值是否被改变,直接赋予新值然后返回被替换的值。
存储
前缀为Store
- func StoreInt32(addr *int32, val int32)
- func StoreInt64(addr *int64, val int64)
- func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
- func StoreUint32(addr *uint32, val uint32)
- func StoreUint64(addr *uint64, val uint64)
- func StoreUintptr(addr *uintptr, val uintptr)
此类操作确保了写变量的原子性,避免其他操作读到了修改变量过程中的脏数据。