• Go语言之协程和管道


    一、协程

    1.Go主线程也可称为线程,也可以理解为进程,一个Go主线程上可以起多个协程,可以理解协程为轻量级的线程,资源消耗较小
    2.协程特点:有独立的栈空间;共享程度堆空间;调度由用户控制;是轻量级的线程

    package main
    
    import (
    	"fmt"
    	"strconv"
    	"time"
    )
    
    /**
    main主线程和test协程同时执行
    */
    func main() {
    	go test() //开启协程
    	for i := 0; i < 10; i++ {
    		fmt.Println("hello wrold ", strconv.Itoa(i+1), "次")
    		time.Sleep(time.Second)
    	}
    }
    
    func test() {
    	for i := 0; i < 10; i++ {
    		fmt.Println("hello golang ", strconv.Itoa(i+1), "次")
    		time.Sleep(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
    • 24
    • 25

    二、管道

    1.为什么需要channel

    主线程等待所有goroutine全部完成时间很难确定,等待时间短协程会随主线程的退出而销毁,等待时间长,不利程序运行。另外全局变量加锁同步实现通讯,也不利于多协程对全局变量的读写操作。

    2.channel的介绍

    • 本质是一个数据结构----队列
    • 线程安全,多协程访问时,不需要加锁
    • 是有类型的,一个string的channel只能存放string类型数据
    • 是引用类型,必须初始化make后才能写入数据
    • 如果写入管道数据大于容量并且没有读该管道会阻塞,如果有读即使读写频率不一致,也不会阻塞
    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	//声明一个可读可写管道,初始化管道必须make
    	var intChan chan int
    	intChan = make(chan int, 3)
    	//声明为只能写
    	var chan1 chan<- int
    	chan1 = make(chan int, 3)
    	chan1 <- 11
    	//声明为只能读
    	//var chan2 <-chan int
    
    	//向管道写入数据,写入数据不能超过长度
    	intChan <- 10
    	num := 211
    	intChan <- num
    	intChan <- 985
    
    	//向管道读取数据,如果已经没有数据在读取,会报deadlock错误
    	n := <-intChan
    	fmt.Println("取出的数据", n)
    
    	//查看管道长度和容量
    	fmt.Printf("长度=%v,容量=%v\n", len(intChan), cap(intChan))
    
    	//关闭channel,关闭后只能读,不能写入
    	close(intChan)
    
    	//channel遍历,不能用普通的for循环
    	//必须关闭channel,否则报错
    	for i := range intChan {
    		fmt.Println(i)
    	}
    
    	intChan1 := make(chan int, 10)
    	strChan := make(chan string, 10)
    	for i := 0; i < 10; i++ {
    		intChan1 <- i
    		strChan <- "hello" + fmt.Sprintf("%d", i)
    	}
    	selectChannel(intChan1, strChan)
    }
    
    //不使用close()也可以从管道中取出数据
    //实际开发中,不好确定什么时候关闭管道,可以用select方式解决
    func selectChannel(intChan1 chan int, strChan chan string) {
    	for {
    		select {
    		//如果intChan一直没有关闭,不会一直堵塞至死锁,自动跳到下一个case
    		case v := <-intChan1:
    			fmt.Printf("从intChan1中取数据%d\n", v)
    		case v := <-strChan:
    			fmt.Printf("从strChan中取数据%s\n", v)
    		default:
    			fmt.Println("都取不到了")
    			return
    		}
    	}
    }
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    三、协程与管道综合应用

    package main
    
    import "fmt"
    
    func writeData(intChan chan int) {
    
    	for i := 0; i < 50; i++ {
    		intChan <- i
    		fmt.Println("写入的数据", i)
    	}
    	//没关闭前读线程已经开始工作
    	//如果没有关闭,编译器会一直等待下一个写入的内容,导致死锁
    	close(intChan)
    
    }
    func readData(intChan chan int, exitChan chan bool) {
    	//如果此协程发生错误,捕获异常,不影响其他协程,主线程不受影响
    	defer func() {
    		//捕获readData抛出的panic
    		err := recover()
    		if err != nil {
    			fmt.Println("发生错误")
    		}
    	}()
    	for {
    		v, ok := <-intChan
    		if !ok {
    			break
    		}
    		fmt.Println("读取到的数据", v)
    	}
    	exitChan <- true
    	close(exitChan)
    }
    func main() {
    	intChan := make(chan int, 50)
    	//借助此管道判断两个协程什么时候结束
    	exitChan := make(chan bool, 1)
    	go writeData(intChan)
    	go readData(intChan, exitChan)
    	//解决主线程等待协程时间问题
    	for {
    		_, ok := <-exitChan
    		if !ok {
    			break
    		}
    	}
    }
    
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
  • 相关阅读:
    CSS基本讲解与使用(详解)
    Python大数据之Python进阶(七)线程的注意点
    OSM+three.js打造3D城市
    URL中的参数提取
    metersphere做接口自动化测试怎样动态修改请求报文字段用java
    系列二、什么是OOM?什么是StackOverflowError?有哪些方法分析?
    库克对iPhone15充满信心,囤货8600万,却被中国5G手机气晕了
    如何获取HuggingFace的Access Token;如何获取HuggingFace的API Key
    火山引擎、腾讯们,看透了大模型这场“猫鼠游戏”
    德鲁克《卓有成效的管理者》学习笔记-掌握时间的学习和实践
  • 原文地址:https://blog.csdn.net/Dreaming_My_Dream/article/details/125524364