基础语法·下(golang笔记第三期)

p.s.这是萌新自己自学总结的笔记,如果想学习得更透彻的话还是请去看大佬的讲解

目录

goroutine并发

goroutine 是 Go 语言中的轻量级线程,由 Go 运行时(runtime)管理,允许在程序中并发地执行函数。

主要特点:

轻量级:

相比操作系统线程,goroutine 开销极小(初始栈大小约 2KB,可按需增长/收缩)

可轻松创建数十万甚至上百万个 goroutine

并发模型:

采用 CSP(Communicating Sequential Processes)并发模型

通过 channel 进行 goroutine 间的通信

实现"不要通过共享内存来通信,而要通过通信来共享内存"

调度机制:

由 Go 运行时调度,采用 M:N 调度模型

多个 goroutine 在少量操作系统线程上复用

调度是协作式的,goroutine 会在特定点(如 I/O 操作、channel 操作)主动让出 CPU
线程在切换中会有时间成本,进程/线程的数量越多,切换成本就越大,也就越浪费

并且多线程的同步竞争问题(如锁,资源竞争冲突)会导致CPU的高消耗调度CPU与高内存占用

缺点:一个协程阻塞会导致其他协程一直等待

实现

go 复制代码
package main

import (
	"fmt"
	"time"
)

func newTask() {
	i := 0
	for {
		i++
		fmt.Printf("new Goroutine : i = %d\n", i)
		time.Sleep(1 * time.Second)
	}
}

//主goroutine
func main() {
	//创建一个go程 去执行newTask() 流程
	go newTask()//次goroutine

	i := 0

	for {
		i++
		fmt.Printf("main goroutine: i = %d\n", i)
		time.Sleep(1 * time.Second)
	}
}
go 复制代码
package main

import (
	"fmt"
	"time"
)
func main() {
    /*
    //用go创建承载一个形参为空,返回值为空的一个函数
    go func() {
        defer fmt.Println("A.defer")

        func() {
            defer fmt.Println("B.defer")
            //退出当前goroutine
            runtime.Goexit()
            fmt.Println("B")
        }()

        fmt.Println("A")
    }()

    //死循环
    for {
        time.Sleep(1 * time.Second)
    }
    */

    go func(a int, b int) bool {
        fmt.Println("a = ", a, " b = ", b)
        return true
    }(10, 20)

    //死循环
    for {
        time.Sleep(1 * time.Second)
    }
}

Channel-线程间通信工具

无缓冲Channel,可以实现同步线程的效果

go 复制代码
package main

import "fmt"

func main() {
    //定义一个channel
    c := make(chan int)

    go func() {
        defer fmt.Println("goroutine结束")

        fmt.Println("goroutine 正在运行...")
        c <- 666 //将666 发送给c
    }()

    num := <-c //从c中接受数据,并赋值给num
    fmt.Println("num = ", num)
    fmt.Println("main goroutine 结束...")
}

而有缓冲区的Channel可以使两个线程分别进行传递与接收操作,即异步通信

go 复制代码
package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int, 3) //带有缓冲的channel

    fmt.Println("len(c) = ", len(c), ", cap(c)", cap(c))

    go func() {
        defer fmt.Println("子go程结束")

        for i := 0; i < 3; i++ {
            c <- i
            fmt.Println("子go程正在运行,发送的元素=", i, " len(c)=", len(c), ", cap(c)=", cap(c))
        }
    }()

    time.Sleep(2 * time.Second)

    for i := 0; i < 3; i++ {
        num := <-c //从c中接收数据,并赋值给num
        fmt.Println("num = ", num)
    }

    fmt.Println("main 结束")
}

输出:

复制代码
root@85db594393fb:/app# go run main.go
len(c) =  0 , cap(c) 3
子go程正在运行,发送的元素= 0  len(c)= 1 , cap(c)= 3
子go程正在运行,发送的元素= 1  len(c)= 2 , cap(c)= 3
子go程正在运行,发送的元素= 2  len(c)= 3 , cap(c)= 3
子go程结束
num =  0
num =  1
num =  2
main 结束
root@85db594393fb:/app# 

注意:当channel已经满,再向里面写数据,就会阻塞;当channel为空,从里面取数据也会阻塞


关闭channel

go 复制代码
package main

import "fmt"

func main() {
    c := make(chan int)

    go func() {
        for i := 0; i < 5; i++ {
            c <- i
        }

        //close可以关闭一个channel
        close(c)
    }()

    for {
        //ok如果为true表示channel没有关闭,如果为false表示channel已经关闭
        if data, ok := <-c; ok {
            fmt.Println(data)
        } else {
            break
        }
    }

    fmt.Println("Main Finished..")
}

channel不像文件一样需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显式的结束range循环之类的,才去关闭channel;

关闭channel后,无法向channel 再发送数据(引发 panic 错误后导致接收立即返回零值);

关闭channel后,可以继续从channel接收数据;

对于nil channel,无论收发都会被阻塞

一些配合

go 复制代码
//可以使用range来迭代不断操作channel
for data := range c {
    fmt.Println(data)
}
----------------------------------------------------------
//单流程下一个go只能监控一个channel的状态,select可以完成监控多个channel的状态
select {
case <-chan1:
    // 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
    // 如果能向chan2写入数据,则进行该case处理语句
default:
    // 如果上面都没有成功,则进入default处理流程
}

Go Module

Go Module 是 Go 语言官方标准、内置于工具的依赖管理与项目构建系统。​ 它彻底取代了旧的 GOPATH模式,允许你将项目放在计算机的任何位置,并能精确、可重现地管理项目依赖的库及其版本。

核心概念

模块:一个模块就是一个项目的集合,它包含了一组相关的 Go 包,并带有一个定义该模块自身信息的 go.mod文件。你的项目本身就是一个模块。

go.mod文件:这是模块的"身份证"和"清单",位于项目的根目录。它定义了:

模块自身的路径(如 module github.com/yourname/project)。

项目所依赖的其他模块及其确切的版本号(如 require github.com/gin-gonic/gin v1.9.1)。

Go 语言的最低版本要求。

go.sum文件:这是模块的"安全锁",由工具自动生成和维护。它记录了每个依赖库的加密哈希值,确保你每次下载的依赖代码都与记录完全一致,防止被篡改,保证构建的可重现性。

核心优势(相比 GOPATH)

项目位置自由:代码可以放在任何地方,如 ~/Desktop/my-go-project。

精确的版本控制:通过语义化版本(如 v1.2.3)明确指定依赖版本,构建稳定可靠。

依赖隔离:每个项目(模块)独立管理自己的依赖,不同项目可以使用同一个库的不同版本,互不干扰。

可重现的构建:任何人拿到你的项目代码,执行 go mod tidy和 go build,都能得到完全相同的依赖树和一致的构建结果。

相关推荐
FakeOccupational2 小时前
【电路笔记 通信】IEEE 1588精密时间协议(PTP):数学假设+时间同步链路建模+消除主从偏差算法
笔记·算法
客卿1232 小时前
最小生成树(贪心)--构造回文串(字符串 + 回文判断 + 构造)
java·开发语言·算法
Bert.Cai2 小时前
Python input函数作用
开发语言·python
云边散步2 小时前
godot2D游戏教程系列二(23)
笔记·学习·游戏·音视频·游戏开发
88号技师2 小时前
2026年3月中科院一区SCI-赏金猎人优化算法Bounty Hunter Optimizer-附Matlab免费代码
开发语言·算法·数学建模·matlab·优化算法
weixin_464307632 小时前
QT宏、属性系统
开发语言·qt
2201_756206342 小时前
STM32F407 + ML307 阿里云物联网项目总结
c语言·开发语言·嵌入式硬件
小糯米6012 小时前
C++ 单调栈原理与模板
开发语言·c++·算法
东北甜妹2 小时前
Python脚本
java·开发语言·前端