• Go中的管道与结构体


    在这里插入图片描述

    管道

    管道底层是一个环形队列(先进先出);

    管道是Go语言在语言级别上提供的goroutine间的通讯方式,我们可以使用channel在多个goroutine之间传递消息。channel是进程内的通讯方式,是不支持跨进程通信的,如果需要进程间通讯的话,可以使用Socket等网络方式。

    管道的声明与初始化

    	var ch chan int //声明一个管道 (先进先出)
    	//初始化管道
    	ch = make(chan int, 9)//实例化一个管道
    
    • 1
    • 2
    • 3

    添加元素

    package main
    
    import "fmt"
    
    func channelTest() {
    	var ch chan int //声明一个管道 (先进先出)
    	//初始化管道
    	ch = make(chan int, 9)//实例化一个管道
    	fmt.Println(ch)
    	//向管道中加入数据 向队尾加入数据 
    	ch <- 1
    	ch <- 2
    	ch <- 3
    	//取出数据 从队尾取出数据
    	value := <-ch
    	fmt.Println(value)
    	value = <-ch
    	fmt.Println(value)
    	value = <-ch
    	fmt.Println(value)
    	ch<-100
    	ch<-200
    	ch<-300
    	close(ch)
    	//管道的遍历  遍历之前必须关闭管道否则会报错---fatal error: all goroutines are asleep - deadlock!
    	for els := range ch {
    		fmt.Println(els)
    	}
    }
    
    func main() {
    	channelTest()
    }
    
    
    • 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

    取出元素

    //取出数据 从队尾取出数据
    	value := <-ch
    	fmt.Println(value)
    	value = <-ch
    	fmt.Println(value)
    	value = <-ch
    	fmt.Println(value)
    	ch<-100
    	ch<-200
    	ch<-300
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    管道的遍历

    1,使用range的方式,需要先关闭管道
    2,使用普通方式:

    func main() {
    
    	var g chan int
    	g = make(chan int, 10)
    
    	for i := 0; i < cap(g); i++ {
    		g <- 10
    	}
    
    	//遍历管道;
    	for i := 0; i < len(g); i++ {
    		v := <-g
    		fmt.Println(v)
    	}
    
    	close(g)
    	//这里应该是空,因为已经被取走了
    	for i2 := range g {
    	
    		fmt.Println(i2)
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    管道一旦关闭,就不能再往里面添加数据了

    管道的阻塞

    如果管道中没有数据,那么从管道中读取数据会导致程序阻塞,直到有数据–

    var ch chan int
    
    func putM(ch chan int) {
    	ch <- 100
    	ch <- 2
    	ch <- 200
    }
    func getM(ch chan int) {
    
    	v := <-ch
    	fmt.Println(v)
    	v = <-ch
    	fmt.Println(v)
    	v = <-ch
    	fmt.Println(v)
    	v = <-ch
    	fmt.Println(v)
    }
    
    func main() {
    	var ch chan int
    	ch = make(chan int, 10)
    	ch <- 1
    	ch <- 2
    	ch <- 3
    	putM(ch)
    	getM(ch)
    }
    
    
    • 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

    在运行阶段,go会直接抛一个异常

    fatal error: all goroutines are asleep - deadlock!
    
    goroutine 1 [chan send]:
    main.putM(...)
    	D:/GoData/eight/ChannelMain.go:40
    main.main()
    	D:/GoData/eight/ChannelMain.go:60 +0x90
    
    Process finished with the exit code 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这是因为目前只有一个线程去管道里读取数据,多了的没办法拿走,此时会产生一个死锁出来;

    在看一个代码:一个往管道里面放,另一个来取

    package main
    
    import (
    	"fmt"
    	"math/rand"
    	"time"
    )
    
    const name = "小明"
    
    // 数据生产者
    func producer(header string, channel chan<- string) {
    	fmt.Println(name)
    	for {
    		channel <- fmt.Sprintln("%s:%v", header, rand.Int31())
    		time.Sleep(time.Second)
    	}
    }
    
    //数据消费者
    
    func customer(channel <-chan string) {
    	for {
    		message := <-channel
    		fmt.Println(message)
    	}
    }
    
    /*
    *
    这是程序执行的入口
    */
    func main() {
    	channel := make(chan string) //实例化一个子字符串通道
    	go producer("cat", channel)
    	go producer("dog", channel)
    	customer(channel)
    
    }
    
    
    • 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

    Go中的结构体

    go中结构体的定义:

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    // 定义一个结构体   匿名结构体 
    var man struct {
    	name string
    	age  int
    }
    
    //定义了一个结构体类型为Human
    
    
    type Human struct {
    	name       string
    	age        int
    	birthday   time.Time
    	country    string
    	addr, love string
    }
    
    //为结构体编写一个方法
    
    //这样我们也可以向java那样编写一个类似于类这样一个...
    
    func (H Human) Say(word string) {
    fmt.Println(man)
    	fmt.Println(H.name, H.age, H.birthday, H.country, "说", word)
    
    }
    
    func main() {
    
    	//准备一个结构体
    
    	xm := Human{name: "小明", age: 19, birthday: time.Now(), country: "China"}
    
    	xm.Say("我爱中国")
    }
    
    
    • 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

    go中的结构体是面向对象编程的一个关键点;

    Go中结构体指针

    在go中我们可以返回一个结构指针,这么做的目的其实是为了避免值的拷贝;

    例如:

    取得结构体的指针方式:

    
    func test() {
    	//声明一个结构体为dog
    	var dog Animal
    	//获得这个结构体的指针 即d是一个指针变量
    	d := &dog
    	fmt.Printf("%v %p\n",d,d)
    	//获得这个结构体的指针 即d是一个指针变量
    	d = &Animal{
    
    	}
    	fmt.Printf("%v %p\n",d,d)
    
    	//也可以为这个结构体中的属性赋值
    	d = &Animal{
    		name: "王啊啊",
    		age:  2,
    	}
    	fmt.Printf("%v %p\n",d,d)
    	//或者可以通过new()函数
    	//通过new()函数来获取指针
    	d=new(Animal)
    	fmt.Printf("%v %p\n",d,d)
    }
    
    
    • 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

    观察结果我们可以看到,当我们不赋值的时候
    在这里插入图片描述

    d := &dog
    d = &Animal{
    
    	}
    
    • 1
    • 2
    • 3
    • 4

    是等价的;

    结构体构造函数

    通过结构体,有了类型上的区分,有了类型上的区分就有了类似java中的构造函数:

    //这个可以看作是构造函数
    
    func NewPerson(name string ,age int) *Person  {
    	return &Person{
    		name: "小明",
    		age:  10,
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试一下代码:

    //这个可以看作是构造函数
    //返回的是Person的值
    func NewPerson(name string, age int) *Person {
    	return &Person{
    		name: name,
    		age:  age,
    	}
    }
    
    
    func main(){
    	fmt.Println("-----------------------------------")
    	fmt.Println(NewPerson("小红", 22))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    返回值是指针和不是指针的区别:
    首先对于基础的类型,没有什么太大影响,如果是对于引用类型,则就有很大的影响:

    举个例子—>

    在这里插入图片描述
    源代码如下,

    // 声明的全局变量
    var name = "小花"
    var age = 17
    
    func main() {
    
    	fmt.Println(NewPerson(name , age ))
    	fmt.Println(newPerson(name , age ))
    	fmt.Println("\n")
    
    
    	fmt.Printf("%s %d name的地址%p age的地址%p\n", name, age, &name, &age)
    	fmt.Printf("name的地址 %p age的地址 %p\n", &(NewPerson(
    		name,
    		age,
    	).name), &(NewPerson(
    		name,
    		age,
    	).age))
    
    	//fmt.Printf("name的地址 %p age的地址 %p\n", &(newPerson(name, age).name), &(newPerson(name, age).age))
    
    }
    
    // 这个可以看作是构造函数
    
    func NewPerson(name string, age int) *Person {
    	//fmt.Printf("%s %d name的地址%p age的地址%p\n", name, age, &name, &age)
    	return &Person{
    		name: name,
    		age:  age,
    	}
    }
    
    func newPerson(name string, age int) Person {
    	//fmt.Printf("%s %d name的地址%p age的地址%p\n", name, age, &name, &age)
    	return Person{
    		name: name,
    		age:  age,
    	}
    
    }
    
    
    
    • 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

    如果结构体中含有匿名变量,
    像这样:
    在这里插入图片描述
    只能有一个 可以不写名字的同类型变量;

    在这里插入图片描述
    可以看到 对于下划线的不配拥有内存空间

    func fun()  {
    	//niming.string="你哈珀"
    	//niming.int=127
    	niming.name="匿名"
    	fmt.Printf(" string %p\n ",&niming.string)
    	fmt.Printf(" int %p\n ",&niming.int)
    	fmt.Printf(" name %p\n ",&niming.name)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

  • 相关阅读:
    范数Norm-衡量向量大小的方法
    WebSocket封装(TypeScript、单例模式、自动重连、事件监听、Vue中使用)
    RISC-V 特权指令结构
    为机器学习模型设置最佳阈值:0.5是二元分类的最佳阈值吗
    笔试强训48天——day8
    手把手教你使用 pprof
    LeetCode每日一题——792. 匹配子序列的单词数
    【AGC】.p12证书文件如何获取MD5
    ASP.NET MVC企业级程序设计 (接上个作品加了添加)
    使用jupyter运行sympy库
  • 原文地址:https://blog.csdn.net/weixin_54061333/article/details/126333052