网站首页 > 文章中心 > 其它

go语言系列介绍_go语言基础

作者:小编 更新时间:2023-09-22 14:02:11 浏览量:331人看过

Go语言基础语法(一)

本文介绍一些Go语言的基础语法.

先来看一个简单的go语言代码:

go语言的注释方法:

代码执行结果:

下面来进一步介绍go的基础语法.

go语言中格式化输出可以使用 fmt 和 log 这两个标准库,

常用方法:

示例代码:

执行结果:

更多格式化方法可以访问中的fmt包.

log包实现了简单的日志服务,也提供了一些格式化输出的方法.

下面来介绍一下go的数据类型

下表列出了go语言的数据类型:

int、float、bool、string、数组和struct属于值类型,这些类型的变量直接指向存在内存中的值;slice、map、chan、pointer等是引用类型,存储的是一个地址,这个地址存储最终的值.

常量是在程序编译时就确定下来的值,程序运行时无法改变.

Go 语言的运算符主要包括算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符以及指针相关运算符.

算术运算符:

关系运算符:

逻辑运算符:

位运算符:

赋值运算符:

指针相关运算符:

下面介绍一下go语言中的if语句和switch语句.另外还有一种控制语句叫select语句,通常与通道联用,这里不做介绍.

if语法格式如下:

if ... else :

else if:

语法格式:

另外,添加 fallthrough 会强制执行后面的 case 语句,不管下一条case语句是否为true.

下面介绍几种循环语句:

也可以通过标记退出循环:

--THE END--

GO语言学习系列八——GO函数(func)的声明与使用

GO是编译性语言,所以函数的顺序是无关紧要的,为了方便阅读,建议入口函数 main 写在最前面,其余函数按照功能需要进行排列

GO的函数 不支持嵌套,重载和默认参数

GO的函数 支持 无需声明变量,可变长度,多返回值,匿名,闭包等

GO的函数用 func 来声明,且左大括号 { 不能另起一行

一个简单的示例:

输出为:

参数:可以传0个或多个值来供自己用

返回:通过用 return 来进行返回

上面就是一个典型的多参数传递与多返回值

对例子的说明:

按值传递:是对某个变量进行复制,不能更改原变量的值

在上例中,返回值 (d int, e int, f int) { 是进行了命名,如果不想命名可以写成 (int,int,int){ ,返回的结果都是一样的,但要注意:

当返回了多个值,我们某些变量不想要,或实际用不到,我们可以使用 _ 来补位,例如上例的返回我们可以写成 d,_,f := test(a,b,c) ,我们不想要中间的返回值,可以以这种形式来舍弃掉

在参数后面以 变量 ... type 这种形式的,我们就要以判断出这是一个可变长度的参数

在上例中, strs ...string 中, strs 的实际值是b,c,d,e,这就是一个最简单的传递可变长度的参数的例子,更多一些演变的形式,都非常类似

在GO中 defer 关键字非常重要,相当于面相对像中的析构函数,也就是在某个函数执行完成后,GO会自动这个;

如果在多层循环中函数里,都定义了 defer ,那么它的执行顺序是先进后出;

当某个函数出现严重错误时, defer 也会被调用

输出为

这是一个最简单的测试了,当然还有更复杂的调用,比如调试程序时,判断是哪个函数出了问题,完全可以根据 defer 打印出来的内容来进行判断,非常快速,这种留给你们去实现

一个函数在函数体内自己调用自己我们称之为递归函数,在做递归调用时,经常会将内存给占满,这是非常要注意的,常用的比如,快速排序就是用的递归调用

本篇重点介绍了GO函数(func)的声明与使用,下一篇将介绍GO的结构 struct

GO语言(三十):访问关系型数据库(上)

本教程介绍了使用 Godatabase/sql及其标准库中的包访问关系数据库的基础知识.

您将使用的database/sql包包括用于连接数据库、执行事务、取消正在进行的操作等的类型和函数.

在本教程中,您将创建一个数据库,然后编写代码来访问该数据库.您的示例项目将是有关老式爵士乐唱片的数据存储库.

首先,为您要编写的代码创建一个文件夹.

①.、打开命令提示符并切换到您的主目录.

在 Linux 或 Mac 上:

在 Windows 上:

运行go mod init命令,为其提供新代码的模块路径.

此命令创建一个 go.mod 文件,您添加的依赖项将在其中列出以供跟踪.

注意: 在实际开发中,您会指定一个更符合您自己需求的模块路径.有关更多信息,请参阅一下文章.

GO语言(二十五):管理依赖项(上)

GO语言(二十六):管理依赖项(中)

GO语言(二十七):管理依赖项(下)

此时此刻呢,您将创建一个数据库.

在此步骤中,您将创建要使用的数据库.您将使用 DBMS 本身的 CLI 创建数据库和表,以及添加数据.

您将创建一个数据库,其中包含有关黑胶唱片上的老式爵士乐录音的数据.

这里的代码使用MySQL CLI,但大多数 DBMS 都有自己的 CLI,具有类似的功能.

①.、打开一个新的命令提示符.

在命令行,登录到您的 DBMS,如下面的 MySQL 示例所示.

将以下 SQL 代码粘贴到文件中,然后保存文件.

在此 SQL 代码中:

(1)删除名为album表. 首先执行此命令可以让您更轻松地稍后重新运行脚本.

您将使用以下形式的source命令:

此时此刻呢,您将编写一些 Go 代码进行连接,以便进行查询.

现在你已经有了一个包含一些数据的数据库,开始你的 Go 代码.

找到并导入一个数据库驱动程序,该驱动程序会将您通过database/sql包中的函数发出的请求转换为数据库可以理解的请求.

①.、在您的浏览器中,访问SQLDrivers wiki 页面以识别您可以使用的驱动程序.

在此代码中:

(1)将您的代码添加到main包中,以便您可以独立执行它.

导入驱动程序后,您将开始编写代码以访问数据库.

现在编写一些 Go 代码,让您使用数据库句柄访问数据库.

您将使用指向结构的指针sql.DB,它表示对特定数据库的访问.

编写代码

①.、进入 main.go,在import您刚刚添加的代码下方,粘贴以下 Go 代码以创建数据库句柄.

该Config结构使代码比连接字符串更容易阅读.

为了简化代码,您调用log.Fatal结束执行并将错误打印到控制台.在生产代码中,您会希望以更优雅的方式处理错误.

文件的顶部现在应该如下所示:

①.、开始跟踪 MySQL 驱动程序模块作为依赖项.

使用go get 添加 github.com/go-sql-driver/mysql 模块作为您自己模块的依赖项.使用点参数表示"获取当前目录中代码的依赖项".

连接成功了!

此时此刻呢,您将查询一些数据.

【golang详解】go语言GMP(GPM)原理和调度

Goroutine调度是一个很复杂的机制,下面尝试用简单的语言描述一下Goroutine调度机制,想要对其有更深入的了解可以去研读一下源码.

首先介绍一下GMP什么意思:

G ----------- goroutine: 即Go协程,每个go关键字都会创建一个协程.

M ---------- thread内核级线程,所有的G都要放在M上才能运行.

P ----------- processor处理器,调度G到M上,其维护了一个队列,存储了所有需要它来调度的G.

Goroutine 调度器P和 OS 调度器是通过 M 结合起来的,每个 M 都代表了 1 个内核线程,OS 调度器负责把内核线程分配到 CPU 的核上执行

模型图:

避免频繁的创建、销毁线程,而是对线程的复用.

①.)work stealing机制

当本线程无可运行的G时,尝试从其他线程绑定的P偷取G,而不是销毁线程.

如果有空闲的P,则获取一个P,继续执行G0.

如果没有空闲的P,则将G0放入全局队列,等待被其他的P调度.然后M0将进入缓存池睡眠.

如下图

GOMAXPROCS设置P的数量,最多有GOMAXPROCS个线程分布在多个CPU上同时运行

在Go中一个goroutine最多占用CPU 10ms,防止其他goroutine被饿死.

具体可以去看另一篇文章

【Golang详解】go语言调度机制 抢占式调度

当创建一个新的G之后优先加入本地队列,如果本地队列满了,会将本地队列的G移动到全局队列里面,当M执行work stealing从其他P偷不到G时,它可以从全局G队列获取G.

协程经历过程

我们创建一个协程 go func()经历过程如下图:

说明:

G只能运行在M中,一个M必须持有一个P,M与P是1:1的关系.M会从P的本地队列弹出一个可执行状态的G来执行,如果P的本地队列为空,就会想其他的MP组合偷取一个可执行的G来执行;

一个M调度G执行的过程是一个循环机制;会一直从本地队列或全局队列中获取G

上面说到P的个数默认等于CPU核数,每个M必须持有一个P才可以执行G,一般情况下M的个数会略大于P的个数,这多出来的M将会在G产生系统调用时发挥作用.类似线程池,Go也提供一个M的池子,需要时从池子中获取,用完放回池子,不够用时就再创建一个.

work-stealing调度算法:当M执行完了当前P的本地队列队列里的所有G后,P也不会就这么在那躺尸啥都不干,它会先尝试从全局队列队列寻找G来执行,如果全局队列为空,它会随机挑选另外一个P,从它的队列里中拿走一半的G到自己的队列中执行.

如果一切正常,调度器会以上述的那种方式顺畅地运行,但这个世界没这么美好,总有意外发生,以下分析goroutine在两种例外情况下的行为.

Go runtime会在下面的goroutine被阻塞的情况下运行另外一个goroutine:

用户态阻塞/唤醒

系统调用阻塞

当M执行某一个G时候如果发生了阻塞操作,M会阻塞,如果当前有一些G在执行,调度器会把这个线程M从P中摘除,然后再创建一个新的操作系统的线程(如果有空闲的线程可用就复用空闲线程)来服务于这个P.当M系统调用结束时候,这个G会尝试获取一个空闲的P执行,并放入到这个P的本地队列.如果获取不到P,那么这个线程M变成休眠状态, 加入到空闲线程中,然后这个G会被放入全局队列中.

队列轮转

可见每个P维护着一个包含G的队列,不考虑G进入系统调用或IO操作的情况下,P周期性的将G调度到M中执行,执行一小段时间,将上下文保存下来,然后将G放到队列尾部,然后从队列中重新取出一个G进行调度.

M0

M0是启动程序后的编号为0的主线程,这个M对应的实例会在全局变量rutime.m0中,不需要在heap上分配,M0负责执行初始化操作和启动第一个G,在之后M0就和其他的M一样了

G0

G0是每次启动一个M都会第一个创建的goroutine,G0仅用于负责调度G,G0不指向任何可执行的函数,每个M都会有一个自己的G0,在调度或系统调用时会使用G0的栈空间,全局变量的G0是M0的G0

一个G由于调度被中断,此后如何恢复?

中断的时候将寄存器里的栈信息,保存到自己的G对象里面.当再次轮到自己执行时,将自己保存的栈信息复制到寄存器里面,这样就接着上次之后运行了.

我这里只是根据自己的理解进行了简单的介绍,想要详细了解有关GMP的底层原理可以去看Go调度器 G-P-M 模型的设计者的文档或直接看源码

参考: ()

()

版权声明:倡导尊重与保护知识产权。未经许可,任何人不得复制、转载、或以其他方式使用本站《原创》内容,违者将追究其法律责任。本站文章内容,部分图片来源于网络,如有侵权,请联系我们修改或者删除处理。

编辑推荐

热门文章