GO语言第一篇

本文介绍Go语言的基础环境搭建过程,并详细解析Go语言的并发特性,包括goroutine的概念、使用方式及如何通过channel实现goroutine间的通信。此外还探讨了如何利用runtime包控制并发行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

环境搭建

官方给的大陆地区链接。Windows直接下载msi文件安装,会自动设置好环境变量。

  • IDEA 安装go插件
    打开idea,File –> settings –> plugins
    点击 Browse repositories ,搜索 go ,列表中显示Go LANGUAGE,点击install安装

基础语法

go的数据类型比较少,这里就直接路略过

概述(入门级理解,如果大神有想说的直言)

数组和分片的区别
数组是定长,而分片是可以动态扩容的,初始化的时候有无长度的限制

switch和其他语言相比可以省略break,只执行case不会像其他语言那样贯穿。

select和switch相似,单不会有根据用户的type执行相应的case而是随机执行可用的,如果没有则执行default

对象关键字使用struct而非class

没有继承

有接口概念,但是自定义类型实现接口不在类型尖括号内实现,IDE无法根据现有的接口找到已经实现了的相应自定义类型

函数的参数和返回值相比其他语言位置互换,类的方法一概写在类定义尖括号外面

使用并发中不涉及线程概念(这里仅限于入门指导上不会涉及,底层原来另说),和你想象的其他语言多线程完全不一样,初次尝试似乎就是单线程的轮询上班似的。

并发

首先并行!=并发,并发是逻辑上的并行.

goroutine

goroutine是Go语言中的轻量级线程实现.它是一个普通的函数,只需使用保留字 go 作为开头。
例如:两个goroutine打印1-10

//这个程序是不会输出的,因为go还没执行完成,main已经退出了
func test(){
    for i:=0; i<10 ;i++{
        fmt.Print(i)
    }
}

func main()  {
    go test()
    go test()
}

没有打印结果

为什么没有打印结果
main使用的是主线程,而test()由于还在执行打印,main执行到最后一行时主线程关闭了,因此也不会执行打印。

解决上面的问题可以让main等待一段时间,让test执行,也可以使用信道进行通信.
1)在main的最后加上time.Sleep(time.Second) 停顿一秒
2)使用信道:channel,因为channel的接收方会等待接收到消息时阻塞

打印结果

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

channel

对于channel类似于linux中的管道,在goroutine之间进行通信,信道可以分为无缓冲的和有缓冲的.对于无缓冲的放了数据就需要取出,否则阻塞;对于有缓冲的信道,可以存储对应大小的数据,满了以后就在存数据就类似于非阻塞的,但是当写入信道满了时会发生阻塞,而读出信道空了时会发生阻塞.

  • 无缓冲的信道是一批数据一个一个的「流进流出」
  • 缓冲信道则是一个一个存储,然后一起流出去

eg: ch := make(chan type, value)

使用信道

var ch = make(chan int)
func test(){
    for i:=0; i<10 ;i++{
        fmt.Print(i)
    }
    ch<-0
}
func main()  {
    go test()
    go test()
    <-ch
    <-ch
}

打印结果同上

为什么是串行的结果

对于上面的例子,输出结果跟串行结果一样,why??
因为Go默认所有的goroutine只能在一个线程里(单核)跑.而一个goroutine不阻塞是不会把CPU让让给其他goroutine的.所以执行结果类似于串行.
对于Go中的并行可以使用runtime包中的函数,

实际上,默认Go所有的goroutines只能在一个线程里跑,只有当一个goroutines被阻塞了才会让出CPU给其他goroutines。
而一个goroutine并不相当于一个线程,goroutine的出现正是为了替代原来的线程概念成为最小的调度单位。(一旦运行goroutine时,先去当前线程查找,如果线程阻塞了,则被分配到空闲的线程,若无空闲线程,就新建一个线程。注意的是,当goroutine执行完毕后,线程不会被回收,而是成为了空闲的线程。)
  如果当前goroutine不发生阻塞,它是不会让出CPU给其他goroutine的, 在上面的代码实例中,输出会是一个一个goroutine进行,而如果使用sleep函数,则阻塞掉了当前goroutine, 当前goroutine主动让其他goroutine执行, 所以形成了并发。

1、允许Go使用多核(runtime.GOMAXPROCS)

在信道的样例中 main()下面一行加上如下代码

//使用两个核
    runtime.GOMAXPROCS(2)

上面会抢占式输出
在多核情况下,如果一个goroutine发生阻塞,其他goroutine会在另一个线程(核)上执行

2、手动显式调动(runtime.Gosched)

func test(){
    for i:=0; i<10 ;i++{
         runtime.Gosched() // 显式地让出CPU时间给其他goroutine
        fmt.Print(i)
    }
    ch<-0
}

执行结果:

0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

主动让出CPU时间的方式仍然是在单核里跑。但手工地切换goroutine导致了看上去的“并行”而显得不自然。

runtime

runtime包是goroutine的调度器

关于runtime包几个函数:
Gosched() //让出cpu
NumCPU()//返回当前系统的CPU核数量
GOMAXPROCS() //设置最大的可同时使用的CPU核数
Goexit() //退出当前goroutine(但是defer语句会照常执行)

总结

  默认所有goroutine会在一个原生线程里跑,也就是默认只使用一个CPU核。如果当前goroutine不发生阻塞,它是不会让出CPU时间给其他同线程的goroutines的,这是Go运行时对goroutine的调度,我们也可以使用runtime包来手工调度。
  若我们开启多核,当一个goroutine发生阻塞,Go会自动地把与该goroutine处于同一系统线程的其他goroutines转移到另一个系统线程上去,以使这些goroutines不阻塞。从而实现并行效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值