• Go语言进阶,闭包、指针、并发


    在前面熟悉了常见的基础语法东西之后,我们来学点稍微复杂的东西,这也是很关键的知识,对于提高性能是必不可少的,对于基础知识想要熟悉的,可以先看如下:
    Go语言零基础入门,从安装到运行代码icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_41896770/article/details/127473110
    Go语言零基础入门,捕获错误、slice切片、for循环、testicon-default.png?t=M85Bhttps://blog.csdn.net/weixin_41896770/article/details/127510535

    闭包

    闭包的概念,简单来说就是可访问局部变量,使得局部变量看起来就是全局变量,这种情况就是说不需要定义全局变量,但是我可以访问函数内部的变量(普通情况是访问不了局部变量的)

    1. package main
    2. import "fmt"
    3. // 斐波拉契数列,返回的是一个函数
    4. func fib() func() int {
    5. a, b := 0, 1
    6. return func() int {
    7. a, b = b, a+b
    8. fmt.Println(&a)
    9. return a
    10. }
    11. }
    12. func main() {
    13. f := fib()
    14. for i := 0; i < 6; i++ {
    15. fmt.Println(f())
    16. }
    17. }
    18. /*
    19. 0xc0000b8000
    20. 1
    21. 0xc0000b8000
    22. 1
    23. 0xc0000b8000
    24. 2
    25. 0xc0000b8000
    26. 3
    27. 0xc0000b8000
    28. 5
    29. 0xc0000b8000
    30. 8
    31. */

    当然使用闭包的时候需要注意的是,变量是一个引用,所以我特意打印a变量的地址,可以看出地址没有变化,如果不引用这个a的话,需要做个赋值的操作即可

    1. aa := a
    2. fmt.Println(&aa)
    3. /*
    4. 0xc0000a0010
    5. 1
    6. 0xc0000a0018
    7. 1
    8. 0xc0000a0030
    9. 2
    10. 0xc0000a0038
    11. 3
    12. 0xc0000a0040
    13. 5
    14. 0xc0000a0048
    15. 8
    16. */

    这样的话,这个地址就随着每次的循环而变化。

    指针

    对于新手来说,有的会比较害怕指针,尤其是初学C/C++的时候,会感觉挺复杂的,其实很简单,不要怕,只要理解了指针、内存地址、取值这些概念就好办了。
    比如变量,大家很熟悉,那变量的值需要在内存中存放是吧,那必须要给个地址,既然是变量,意思就是说里面的值是可以变化的,也就是说同一个地址可以随着不同条件而变化,通俗来说就是公交车上的人,经常上上下下,里面的乘客经常在变化。
    获取变量的内存地址就是:&变量,有了地址,想要获取值,那就是:*地址

    1. var a int = 10
    2. var b string = "hello"
    3. var i *int // 定义一个整型的指针
    4. var ii *int
    5. var s *string// 定义一个字符串的指针
    6. i = &a //将变量a的内存地址赋值给i,这个就是形象的说指针i指向a,相当于我要去这个地方找到你
    7. fmt.Printf("%p, %p, %d \n", i, ii, *i)//0xc000122000, 0x0, 10
    8. //知道地址了,取这个内存地址的值:*指针,如果**ii会报错,这个就是没有赋值,是空指针或野指针
    9. /*
    10. panic: runtime error: invalid memory address or nil pointer dereference
    11. */
    12. fmt.Printf("类型:%T \n", s)//类型:*string
    13. s = &b
    14. fmt.Printf("%p, %s", s, *s)//0xc00010c210, hello

    另外比较常见的一种就是指针作为参数的传递,交换值的例子:

    1. package main
    2. import "fmt"
    3. //等价于func swap(a, b *int)类型一样的,可以简化成只需在最后声明类型即可
    4. func swap(a *int, b *int) {
    5. *b, *a = *a, *b
    6. }
    7. func main() {
    8. x, y := 1, 2
    9. fmt.Println(x, y)
    10. swap(&x, &y)
    11. fmt.Println(x, y)
    12. }
    13. /*
    14. 1 2
    15. 2 1
    16. */

    如果不是指针做参数的话,就是常见的值传递,这样就是一个拷贝,就是会复制一份,那对复制的这份无论进行什么操作,都不会影响原来的值是吧,如下:

    1. func swap(a int, b int) {
    2. b, a = a, b
    3. }
    4. func main() {
    5. x, y := 1, 2
    6. fmt.Println(x, y)
    7. swap(x, y)
    8. fmt.Println(x, y)
    9. }
    10. 结果是不会变化的
    11. /*
    12. 1 2
    13. 1 2
    14. */

    当然了,单纯对于交换数据来说,值类型换种写法也是可以搞定了,只不过这里是介绍指针的特性:

    1. func swap(a, b int) (int, int) {
    2. return b, a
    3. }
    4. func main() {
    5. x, y := swap(1, 2)
    6. fmt.Println(x, y)//2 1
    7. }

    并发

    以前处理并发,一般使用线程池,在Go语言中的机制是goroutine,是一种轻量级的线程实现,这种机制是系统会自动的把任务分配给CPU,有Go的运行时管理。
    了解这个goroutine机制,需要熟悉通道,先来看个示例:

    1. package main
    2. import "fmt"
    3. // 将自然数发送到通道ch里面
    4. func Generate(ch chan<- int) {
    5. for i := 1; ; i++ {
    6. ch <- i
    7. }
    8. }
    9. func main() {
    10. ch := make(chan int)// 创建新通道
    11. go Generate(ch) // 这里就是goroutine机制:go 函数
    12. for i := 0; i < 5; i++ {
    13. a := <-ch //将通道里的值拿出来
    14. fmt.Println(a)
    15. }
    16. }
    17. /*
    18. 1
    19. 2
    20. 3
    21. 4
    22. 5
    23. */

    了解了通道,接下来了解并发,看例子:

    1. package main
    2. import (
    3. "fmt"
    4. "time"
    5. )
    6. func f() {
    7. var n int
    8. for {
    9. n++
    10. fmt.Println(n)
    11. time.Sleep(time.Second)
    12. }
    13. }
    14. func main() {
    15. go f()
    16. var s string
    17. fmt.Scanln(&s)
    18. }

    这个例子就是说,一直累加并输出的同时,你可以进行输入,两个同时进行。

    了解了上述内容,将这些结合起来,实现素数筛选的例子:

    1. package main
    2. import "fmt"
    3. // 发送自然数(从2开始)到通道ch
    4. func Generate(ch chan<- int) {
    5. for i := 2; ; i++ {
    6. ch <- i
    7. }
    8. }
    9. // 筛选素数
    10. // 将通道in的值除以得到的素数,不被整除的输出到通道out
    11. func Filter(in <-chan int, out chan<- int, prime int) {
    12. for {
    13. i := <-in // 通道in的值赋给i
    14. if i%prime != 0 {
    15. out <- i // 发送i到通道out
    16. }
    17. }
    18. }
    19. func main() {
    20. ch := make(chan int) // 创建新的通道
    21. go Generate(ch) // 启动goroutine机制
    22. for i := 0; i < 10; i++ {
    23. prime := <-ch
    24. fmt.Println(prime)
    25. ch1 := make(chan int) // 再创建一个通道
    26. go Filter(ch, ch1, prime)
    27. ch = ch1 //将通道ch1筛选出来的素数赋给通道ch
    28. }
    29. }
    30. /*
    31. 2
    32. 3
    33. 5
    34. 7
    35. 11
    36. 13
    37. 17
    38. 19
    39. 23
    40. 29
    41. */
  • 相关阅读:
    算法题每日一练(一)
    Mysql 事务与Spring事务
    微信小程序实现上下手势滑动切换
    基于.NetCore开发博客项目 StarBlog - (18) 实现本地Typora文章打包上传
    【数据结构】11道LeetCode链表OJ练习
    自学Python01-创建文件写入内容
    Linux 文件/目录访问(opendir/closedir/readdir)
    【STM32】【HAL库】【实用制作】数控收音机(软件设计)
    html +css 练习的(太极图)
    MongoDB 索引和常用命令
  • 原文地址:https://blog.csdn.net/weixin_41896770/article/details/127547900