goroutine

goroutine:

本质是协程,是实现并行计算的核心。
goroutine使用方式非常的简单,只需使用go关键字即可启动一个协程,并且它是处于异步方式运行,你不需要等它运行完成以后在执行以后的代码。

启动一个协程

go func()//通过go关键字启动一个协程来运行函数

go协程和channel通信

func NumCPU() int
  NumCPU返回本地机器的逻辑CPU个数。
func GOMAXPROCS(n int) int
  GOMAXPROCS设置可同时执行的最大CPU数,并返回先前的设置。 若 n < 1,它就不会更改当前设置。本地机器的逻辑CPU数可通过 NumCPU 查询。本函数在调度程序优化后会去掉

常用channel操作

//1.创建一个可以存放3个int类型的管道
	var intChan chan int
	intChan = make(chan int, 3)
	//2.了解管道
	fmt.Printf("intChane 的值= %v intChan本身的地址=%p\n", intChan, &intChan)
	//3.向管道写入数据
	intChan <- 10
	num:=211
	intChan <- num
	//注意向管道写入数据不能超过限定长度,不能像动态数组一样.
	//4.看管道的长度和cap(容量)
	fmt.Printf("channel len= %v cap= %v\n",len(intChan),cap(intChan));
	
	//5.从管道中读取数据
	var num2 int
	num2= <-intChan
	fmt.Println(num2)
	fmt.Printf("channel len= %v cap= %v\n",len(intChan),cap(intChan));
	//注意再没有使用协程的情况下数据全部取完就会报错.

      func close(c chan<- Type)
      关闭后后不能写只能读.
      //遍历
      intChan2 := make(chan int, 100)
	for i := 0; i < 50; i++ {
		intChan2 <- i //放入一百个数据管道
	}
	//遍历管道不能使用普通的for循环
	close(intChan2)
	for v := range intChan2 {
		fmt.Printf("v= %d", v)
	}


package main

import (
	"fmt"
	"time"
)

func test() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("Error ")
		}
	}()
	var myMap map[int]string
	myMap[1] = "ddd"
}
func main() {
	go test()
	time.Sleep(time.Second)
}
会输出Error 
存放任意类型
package main
import (
	"fmt"
)
type Cat struct {
	Name string
	Age  int
}
func main() {
	allchan := make(chan interface{}, 3)
	allchan <- 10
	allchan <- "tom jack"
	cat := Cat{"小花猫", 4}
	allchan <- cat

	<-allchan
	<-allchan
	newcat := <-allchan //管道取出的是???
	fmt.Printf("newcat %T ,newcat %v\n", newcat, newcat)
	//fmt.Printf("newcat %T",newcat.Name)会报错
	
	//使用类型断言
	a := newcat.(Cat)
	fmt.Printf("%T", a.Name)
}

案例

案例1

package main
import (
	"fmt"
	"strconv"
	"time"
)
func test(){
	for i:=0;i<=10;i++{
		fmt.Println(" test() hello,world"+strconv.Itoa(i));
		time.Sleep(time.Second)
	}
}
func main() {
	go test();//开启一个协程
	for i:=0;i<=10;i++{
		fmt.Println("main() hello,world"+strconv.Itoa(i));
		time.Sleep(time.Second);
	}
}


案例2


全局变量+锁解决

package main
import (
	"fmt"
	"sync"
	"time"
)

//这里声明全局变量
var(
	myMap=make(map[int]int)
	lock sync.Mutex//互坼锁
)
func test(n int){
	lock.Lock();
	res:=1
	for i:=1;i<=n;i++{
		res*=i;
	}
	myMap[n]=res;
	lock.Unlock();
}
func main() {
	for i:=1;i<=20;i++{
		go test(i)
	}

	time.Sleep(8000)
	
	for i,v:=range myMap {
		lock.Lock();
		fmt.Printf("%d %d\n",i,v);
		lock.Unlock();
	}
}

channel实现思路

本质就是一个数据结构---------FIFO(first in first out)队列(先进先出)
线程安全:多个协程操作管道是线程安全的。

1)前面使用全局变量加锁同步来解决 goroutine 的通讯,但不完美
2)主线程在等待所有goroutine 全部完成的时间很难确定,我们这里设置10秒,仅仅是估算。
3)如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能还有goroutine处于工作状态,这时也会随主线程的退出而销毁
4)通过全局变量加锁同步来实现通讯,也并不利用多个协程对全局变量的读写操作。
5)上面种种分析都在呼唤一个新的通讯机制-channel

案例3


package main
import(
	"fmt")
//write Date
func writeDate(intChan chan int){
	for i:=1;i<=50;i++{
		//放入数据
		intChan<-i
		fmt.Printf("writeDate 写数据=%v\n",i)
	}
	close(intChan);
}
//read Date
func readDate(intChan chan int,exitChan chan bool){
	for{
		v,ok:=<-intChan
		if !ok{
			break
		}
		fmt.Printf("readDate 读到数据=%v\n",v)
	}
	//read_Date 读取完数据后,任务完成
	exitChan<-true
	close(exitChan)
}
func main() {
	//创建两个管道
	intChan:=make(chan int,50)
	exitChan:=make(chan bool,1)
	go writeDate(intChan)
	go readDate(intChan,exitChan);
	for{
		_,ok:=<-exitChan
		if !ok{
			break;
		}
	}
}

阻塞


package main
import(
	"fmt")

func main() {
	//使用select可以解决从管道取数据的阻塞问题
	intChan:=make(chan int,10)
	for i:=0;i<10;i++{
		intChan<-i
	}
	
	stringChan:=make(chan string,5)
	for i:=0;i<5;i++{
		stringChan<-"hello"+fmt.Sprintf("%d",i);
	}
    //label
	label:
	for{
		select {
		case v:=<-intChan://如果管道一直没关闭,不会一直阻塞deadlock
			fmt.Println("从intChan读取的数据%d\n",v)
		case v:=<-stringChan:
			fmt.Println("从stringChan读取的数据%s\n",v)
		default:
			break label
		}
	}
}

素数案例


package main

import (
	"fmt"
	"time"
)

//向 intChan 放入 1-8000个数
func putNum(intChan chan int) {
	for i := 1; i <= 80000; i++ {
		intChan <- i
	}
	//关闭intChan
	close(intChan)
}
func primesNum(intChan chan int, primesChan chan int, exitChan chan bool) {

	for {
		num, ok := <-intChan
		if ok == false {
			break
		}

		flag := false
		//判断素数

		for i := 2; i <= num/i; i++ {
			if num%i == 0 {
				flag = true
				break
			}
		}
		if flag == true {
			primesChan <- num
		}
	}

	//这里还不能关闭primesChan
	exitChan <- true
}
func main() {
	intChan := make(chan int, 800)
	primesChan := make(chan int, 800000) //放入结结果
	//退出标记
	exitChan := make(chan bool, 4) //4个

	start := time.Now().UnixNano()
	//开启协程,用intChan放入 1-8000个数
	go putNum(intChan)

	//开启4个协程,从intChan取数
	for i := 0; i < 4; i++ {
		go primesNum(intChan, primesChan, exitChan)
	}

	for i := 0; i < 4; i++ {
		<-exitChan
	}
	close(exitChan)
	close(primesChan)

	fmt.Println("结束所有协程")
	fmt.Println("耗时毫秒 \n", (time.Now().UnixNano()-start)/1000000)
}
posted @ 2022-05-13 13:57  warmhearthhh  阅读(51)  评论(0编辑  收藏  举报