大数据开发-Go 基础入门-包,函数,通道

本文介绍了Go语言的基础知识,包括包的组织和引用,函数的声明与执行,以及字符串的特性和操作。重点讲解了Go中的通道(Channel)概念,通道的类型、容量、操作方式,以及如何利用通道实现并发中的数据同步。此外,还提到了通道操作的同步特性以及一个简单的非缓冲通道使用示例。
摘要由CSDN通过智能技术生成

包引用和包说明

一个代码包可以由若干Go源文件组成。一个代码包的源文件须都处于同一个目录下。 一个目录(不包含子目录)下的所有源文件必须都处于同一个代码包中,亦即这些源文件开头的package pkgname语句必须一致。 所以,一个代码包对应着一个目录(不包含子目录),反之亦然。 对应着一个代码包的目录称为此代码包的目录,但是规定了对于Go官方工具链来说,含有internal目录名的代码包被视为一个特殊的代码包,它只能被此internal目录的直接父目录(和此父目录的子目录)中的代码包所引入。 比如,代码包.../a/b/c/internal/d/e/f和.../a/b/c/internal只能被引入路径含有.../a/b/c前缀的代码包引入。

init函数

在一个代码包中,甚至一个源文件中,可以声明若干名为init的函数。 这些init函数必须不带任何输入参数和返回结果。

注意:我们不能声明名为init的包级变量、常量或者类型。

在程序运行时刻,在进入main入口函数之前,每个init函数在此包加载的时候将被(串行)执行并且只执行一遍

package main

import "fmt"

func init() {
  fmt.Println("hi,", bob)
}

func main() {
  fmt.Println("bye")
}

func init() {
  fmt.Println("hello,", smith)
}

func titledName(who string) string {
  return "Mr. " + who
}

var bob, smith = titledName("Bob"), titledName("Smith")

在同一个源文件中声明的init函数将按从上到下的顺序被调用执行,

Go中的字符串

字符串类型是Go中的一种重要类型,字符串类型的内部结构声明如下:

type _string struct {

  elements *byte // 引用着底层的字节

  len      int   // 字符串中的字节数

}

和其他语言不同的点,字符串类型都是可比较类型,不需要使用函数,同一个字符串类型的值可以用==和!=比较运算符来比较。 并且和整数/浮点数一样,同一个字符串类型的值也可以用>、<、>=和<=比较运算符来比较。 当比较两个字符串值的时候,它们的底层字节将逐一进行比较。如果一个字符串是另一个字符串的前缀,并且另一个字符串较长,则另一个字符串为两者中的较大者。

由于Go没有类,所以字符串的常见操作放在strings中,aString 的方法可以用来切片和索引,

Go中的通道

Go也支持几种传统的数据同步技术,但是只有通道为一等公民。 通道是Go中的一种类型,所以我们可以无需引进任何代码包就可以使用通道。 几种传统的数据同步技术提供在sync和sync/atomic标准库包中,通道可以认为是Go中的除协程外的第二大特点,

和数组、切片以及映射类型一样,每个通道类型也有一个元素类型。 一个通道只能传送它的(通道类型的)元素类型的值。

通道可以是双向的,也可以是单向的。

  • 字面形式chan T表示一个元素类型为T的双向通道类型。 编译器允许从此类型的值中接收和向此类型的值中发送数据。

  • 字面形式chan<- T表示一个元素类型为T的单向发送通道类型。 编译器不允许从此类型的值中接收数据。

  • 字面形式<-chan T表示一个元素类型为T的单向接收通道类型。 编译器不允许向此类型的值中发送数据。

双向通道chan T的值可以被隐式转换为单向通道类型chan<- T<-chan T,但反之不行(即使显式也不行)。 类型chan<- T<-chan T的值也不能相互转换。

每个通道值有一个容量属性。此属性的意义将在下一节中得到解释。 一个容量为0的通道值称为一个非缓冲通道(unbuffered channel),一个容量不为0的通道值称为一个缓冲通道(buffered channel)。

通道类型的零值也使用预声明的nil来表示。 一个非零通道值必须通过内置的make函数来创建。 比如make(chan int, 10)将创建一个元素类型为int的通道值。 第二个参数指定了欲创建的通道的容量。此第二个实参是可选的,它的默认值为0

通道操作

Go中有五种通道相关的操作。假设一个通道(值)为ch,下面列出了这五种操作的语法或者函数调用。

  1. 调用内置函数close来关闭一个通道:

    close(ch)

    传给close函数调用的实参必须为一个通道值,并且此通道值不能为单向接收的。

  2. 使用下面的语法向通道ch发送一个值v

    ch <- v

    v必须能够赋值给通道ch的元素类型。 ch不能为单向接收通道。 <-称为数据发送操作符。

  3. 使用下面的语法从通道ch接收一个值:

    <-ch

    如果一个通道操作不永久阻塞,它总会返回至少一个值,此值的类型为通道ch的元素类型。 ch不能为单向发送通道。 <-称为数据接收操作符,是的它和数据发送操作符的表示形式是一样的。

    在大多数场合下,一个数据接收操作可以被认为是一个单值表达式。 但是,当一个数据接收操作被用做一个赋值语句中的唯一的源值的时候,它可以返回第二个可选的类型不确定的布尔值返回值从而成为一个多值表达式。 此类型不确定的布尔值表示第一个接收到的值是否是在通道被关闭前发送的。 (从后面的章节,我们将得知我们可以从一个已关闭的通道中接收到无穷个值。)

    数据接收操作在赋值中被用做源值的例子:

    v = <-ch

    v, sentBeforeClosed = <-ch

  4. 查询一个通道的容量:

    cap(ch)

    其中cap是一个已经在容器类型一文中介绍过的内置函数。 cap的返回值的类型为内置类型int

  5. 查询一个通道的长度:

    len(ch)

    其中len是一个已经在容器类型一文中介绍过的内置函数。 len的返回值的类型也为内置类型int。 一个通道的长度是指当前有多少个已被发送到此通道但还未被接收出去的元素值。

Go中大多数的基本操作都是未同步的。换句话说,它们都不是并发安全的。 这些操作包括赋值、传参、和各种容器值操作等。 但是,上面列出的五种通道相关的操作都已经同步过了,因此它们可以在并发协程中安全运行而无需其它同步操作。

注意:通道的赋值和其它类型值的赋值一样,是未同步的。 同样,将刚从一个通道接收出来的值赋给另一个值也是未同步的

一个简单的通过一个非缓冲通道实现的请求/响应的例子:

package main

import (

  "fmt"

  "time"

)

func main() {

  c := make(chan int) // 一个非缓冲通道

  go func(ch chan<- int, x int) {

    time.Sleep(time.Second)

    // <-ch    // 此操作编译不通过

    ch <- x*x  // 阻塞在此,直到发送的值被接收

  }(c, 3)

  done := make(chan struct{})

  go func(ch <-chan int) {

    n := <-ch      // 阻塞在此,直到有值发送到c

    fmt.Println(n) // 9

    // ch <- 123   // 此操作编译不通过

    time.Sleep(time.Second)

    done <- struct{}{}

  }(c)

  <-done // 阻塞在此,直到有值发送到done

  fmt.Println("bye")

}

参考

更多详细内容可以查看 https://gfw.go101.org/article/101.html


吴邪,小三爷,混迹于后台,大数据,人工智能领域的小菜鸟。 更多请关注 ![file](https://img-blog.csdnimg.cn/20210426015150277.png)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值