• 11Go语言中的Goroutine


    区分并发与并行

    并行是程序在任一时刻都同时运行,并发是单位时间内同时运行,实际微观上是有先后的。

    goroutine

    通过go关键字来调用一个goroutine,go关键字后面必须跟一个函数,不能是语句或其他东西,函数返回值忽略

    go+匿名函数型式
    package main
    
    import (
    	"runtime"
    	"time"
    )
    
    func main() {
    	
    	go func(){
    		sum := 0
    		for i := 0; i<10000; i++{
    			sum += i
    		}
    		println(sum)
    		time.Sleep(2*time.Second)
    	}()  //不要忘记最后的括号
    	//相当于
    	sum = func{
    			sum := 0
    			for i := 0; i<10000; i++{
    				sum += i
    			}
    			println(sum)
    			time.Sleep(2*time.Second)
    		}
    	go sum()
    	//runtime.NumGoroutine()显示当前goroutine数目
    	println("NumGoroutine = ", runtime.NumGoroutine())
    
    	time.Sleep(5*time.Second) 
    	//这边是为了方式main routine提前完成,而此时sum runtine还没完成
    	//所以拖了点时间
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    go+有名函数形式
    package main
    
    import (
    	"runtime"
    	"time"
    )
    
    func sum(){
    	sum :=0
    	for i:=0; i<10000; i++{
    		sum += i
    	}
    	println(sum)
    	time.Sleep(1*time.Second)
    }
    
    
    func main() {
    	go sum()
    	println("NumGoroutine = ", runtime.NumGoroutine())
    
    	time.Sleep(5*time.Second)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    goroutine特性

    1.go执行是非阻塞的,不会等待
    2.go后面的函数返回值会被忽略
    3.调度器不能保证多个goroutine执行次序

    	go func(){
    		println(1)
    	}()
    	go func(){
    		println(2)
    	}()
    	go func(){
    		println(3)
    	}()
    //顺序不一定
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4.所有goroutine都是平等调度
    5.go会为main函数创建一个goroutine

    chan

    chan 即channel,通道是goroutine之间通信和同步的重要组件。在go里面不是通过共享内存来通信,而是通过通信来共享内存。
    通道是一个类型,用chan来声明,但简单声明var a chan int 没有实际意义,其值是nil。go中用make来创建通道

    //创建一个无缓冲的通道,通道存放元素的类型为datatype
    make(chan datatype)
    //创建一个有10个缓冲的通道,通道存放元素的类型为datatype
    make(chan datatype, 10)
    
    • 1
    • 2
    • 3
    • 4

    通道分为有缓冲和无缓冲的,内置cap,len函数。无缓冲cap,len都是0, 有缓冲,cap是容量,len是没有被读取的元素数。无缓冲的通道既可以用于通信,也可以用于两个goroutine同步,有缓冲的通道主要用于通信。

    func main() {
    	
    	
    	c := make(chan struct{})
    	ci := make(chan int , 100)
    	go func (i chan struct{}, j chan int)  {
    		for i:=0; i<10; i++{
    			ci <- i
    		}
    		close(ci) //将ci通道关闭
    		c <-struct{}{}
    	}(c, ci)
    
    	println("NumGoroutine=", runtime.NumGoroutine())
    
    	<-c  //读通道c,通过通道进行同步等待
    	//但是我这边还是numgoroutine还是2
    	//需要用sleep5snum变为1
    	
    	//time.Sleep(5*time.Second)
    
    	println("NumGoroutine=", runtime.NumGoroutine())
    	
    	for v := range ci{
    		println(v)
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    chan可能产生的错误

    1. panic:向关闭的通道发送会造成panic,重复关闭通道会导致panic
    2. 阻塞:向未初始化的通道写数据或读数据会导致当前goroutine 永久阻塞;向缓冲区已满的通道写入数据;通道空是,读取数据;
    3. 非阻塞:读取已经关闭的通道不会发送阻塞,但会返回通道元素类型的零值;向有缓冲且没有满的通道读写不会发生阻塞
  • 相关阅读:
    conan 基本配置
    【java_wxid项目】【第四章】【Spring Cloud Ribbon集成】
    Talk Is Cheap,Show Me The Code: 三种语言个人框架压测(Java/Go/Rust)
    【微信小程序】文章样式,标题样式,及设置背景~
    java基于springboot房产门户房屋出租销售网站—计算机毕业设计
    阿里内部热捧“Spring全线笔记”,不止是全家桶,太完整了
    LeetCode 1038.从二叉搜索树到更大和树
    【Html——自由小球球】(效果+代码)
    【开源】基于Vue.js的智能教学资源库系统
    Linux拔网线后网卡仍然处于激活状态
  • 原文地址:https://blog.csdn.net/weixin_45337185/article/details/126550068