go 协程

golang中的并发是函数相互独立运行的能力。Goroutines是并发运行的函数。Golang提供了

  1. 如何实现go协程
    只需要在函数前面加上go即可
go task()
package main

import (
	"fmt"
	"time"
)

func show(msg string) {
	for i := 0; i < 5; i++ {
		fmt.Printf("msg: %v\n", msg)
		time.Sleep(time.Microsecond * 1000)
	}
}

func main() {
	go show("go show") //开辟goroutines中运行
	show("haha")       //这个会在main函数的goroutines中运行
	//注意协程是主进程的守护进程,主进程结束,协程就结束了,不管运没运行完
	fmt.Println("end..........")
}

  1. channel
    它是一个数据管道,可以往里面写数据,从里面读数据。
    channel 是 goroutine 之间数据通信桥梁,而且是线程安全的。
    channel 遵循先进先出原则。
    写入,读出数据都会加锁。
    channel 可以分为 3 种类型:
  • 只读 channel,单向 channel
  • 只写 channel,单向 channel
  • 可读可写 channel

channel 还可按是否带有缓冲区分为:

  • 带缓冲区的 channel,定义了缓冲区大小,可以存储多个数据
  • 不带缓冲区的 channel,只能存一个数据,并且只有当该数据被取出才能存下一个数据
unbuffered:=make(channel int)
buffered:=make(channel int,10)
package main

import (
	"fmt"
	"math/rand"
	"time"
)

var channeltest = make(chan int)

func show(msg string) {
	value := rand.Intn(10)
	channeltest <- value // 往管道中写数据

}

func main() {
	defer close(channeltest) // 关闭管道
	go show("go show")       //开辟goroutines中运行
	time.Sleep(time.Second * 1)
	value := <-channeltest //从channel中获取数据
	fmt.Printf("value: %v\n", value)
	//注意协程是主进程的守护进程,主进程结束,协程就结束了,不管运没运行完
	fmt.Println("end..........")
}
  1. waitgroup实现同步
    类似于.net中的task wait,你可以取消下面wg 的试试看两者的区别,我们添加完wg后发现,a以及b函数都执行完毕
package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func a() {
	defer wg.Done()
	for i := 0; i < 10; i++ {
		fmt.Println("a函数")
		time.Sleep(time.Microsecond * 100)
	}
}

func b() {
	defer wg.Done() //别忘了在一次执行完毕后运行这个函数
	for i := 0; i < 10; i++ {
		fmt.Println("b函数")
		time.Sleep(time.Microsecond * 100)
	}
}

func main() {
	wg.Add(1) //在每一个go之前执行这个行数
	go a() //开辟goroutines中运行
	wg.Add(1)
	go b()
	wg.Wait()
	//注意协程是主进程的守护进程,主进程结束,协程就结束了,不管运没运行完
	fmt.Println("end..........")
}
  1. Mutex
    锁,用来解决同时访问全局变量
package main

import (
	"fmt"
	"runtime"
	"sync"
	"time"
)

var wg sync.WaitGroup
var value int = 100
var lock sync.Mutex

func a() {
	defer wg.Done()
	for i := 0; i < 100; i++ {
		lock.Lock()
		value += 1
		lock.Unlock()
		fmt.Println("a++")
		time.Sleep(time.Microsecond * 100)
	}
}

func b() {
	defer wg.Done()
	for i := 0; i < 100; i++ {
		lock.Lock()
		value -= 1
		lock.Unlock()
		fmt.Println("a--")
		time.Sleep(time.Microsecond * 100)
	}
}

func main() {
	fmt.Printf("runtime.NumCPU(): %v\n", runtime.NumCPU())
	runtime.GOMAXPROCS(8) //最大并发
	wg.Add(1)
	go a()
	wg.Add(1) //开辟goroutines中运行
	go b()
	wg.Wait()
	fmt.Println("end..........")
	fmt.Printf("value: %v\n", value)
}
  1. runtime包
  • runtime.Gosched() 让出CPU,重新等待安排任务
package main

import (
	"fmt"
	"runtime"
)

func a() {

	for i := 0; i < 2; i++ {
		fmt.Println("a函数")
	}

}

func main() {
	fmt.Printf("runtime.NumCPU(): %v\n", runtime.NumCPU())
	runtime.GOMAXPROCS(8) //最大并发
	go a()                //开辟goroutines中运行

	for i := 0; i < 2; i++ {
		runtime.Gosched() //让出CPU,执行A函数
		fmt.Printf("i: %v\n", i)

	}
	fmt.Println("end..........")
}
  • runtime.Goexit() 退出协程
  • runtime.GOMAXPROCS(8) 最大并发
  1. select Switch
  • select是Go中的一个控制结构,类似于Switch语句,用于处理异步IO操作。select会监听语句中channel的读写操作,当case中channel读写操作为非阻塞状态时,将会触发相应的动作
  • select中的case语句必须是一个channel操作
  • select中的default自居总是可运行的
  • 如果有多个case都可以运行,select会随机公平的选出一个执行,其他不会执行
  • 如果没有可执行的case语句,且有default语句,那么会执行default
  • 如果没有可运行的case语句,且没有default语句,select将阻塞,知道某个case可以运行
package main

import (
	"fmt"
	"time"
)

var valueint = make(chan int)
var valurstring = make(chan string)

func main() {

	//关闭通道后会读取类型默认值,如int默认值为0
	go func() {
		valueint <- 100
		valurstring <- "haha"
		defer close(valueint)
		defer close(valurstring)
	}()

	for {
		select {
		case r := <-valueint:
			fmt.Printf("valueint: %v\n", r)
		case r := <-valurstring:
			fmt.Printf("valurstring: %v\n", r)
		default:
			fmt.Println("default .....")
		}
		time.Sleep(time.Second)
	}
}

  1. ticker
    ticker会一直运行,timer只运行一次
package main

import (
	"fmt"
	"runtime"
	"time"
)

var valueint = make(chan int)

func main() {
	ticker := time.NewTicker(time.Second)

	//关闭通道后会读取类型默认值,如int默认值为0
	go func() {
		//每秒钟随机的往通道插入数据
		for _ = range ticker.C {
			select {
			case valueint <- 1:
			case valueint <- 2:
			case valueint <- 3:
			}
		}
	}()
	var sum int
	for v := range valueint {
		fmt.Printf("v: %v\n", v)
		sum += v
		if sum >= 10 {
			runtime.Goexit()
		}
	}
}

你可能感兴趣的:(GO,golang)