go并发编程笔记2|青训营

52 阅读2分钟

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)

此类操作确保了写变量的原子性,避免其他操作读到了修改变量过程中的脏数据。