关于golang:Golang-基础整理-这一篇就够了

6次阅读

共计 14121 个字符,预计需要花费 36 分钟才能阅读完成。

第一个 golang 程序

package main

import "fmt"

func main() {fmt.Println("hello golang")
}

根本数据类型

  1. 布尔型 (true 或者 false)
  2. 数字类型 (整型 int 和 浮点型 float32、float64)
  3. 字符串类型 (字符串就是一串固定长度的字符连接起来的字符序列)
  4. 派生类型:
  • 指针类型(Pointer)
  • 数组类型
  • 结构化类型 (struct)
  • Channel 类型
  • 函数类型
  • 切片类型
  • 接口类型(interface)
  • Map 类型

数字类型

  1. 整形
  • uint8

无符号 8 位整型 (0 到 255)

  • uint16

无符号 16 位整型 (0 到 65535)

  • uint32

无符号 32 位整型 (0 到 4294967295)

  • uint64

无符号 64 位整型 (0 到 18446744073709551615)

  • int8

有符号 8 位整型 (-128 到 127)

  • int16

有符号 16 位整型 (-32768 到 32767)

  • int32

有符号 32 位整型 (-2147483648 到 2147483647)

  • int64

有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

  1. 浮点型
  • float32

32 位浮点型数

  • float64

64 位浮点型数

  • complex64

32 位实数和虚数

  • complex128

64 位实数和虚数

  1. 其余数字类型
  • byte

相似 uint8

  • rune

相似 int32

  • uint

32 或 64 位

  • int

与 uint 一样大小

  • uintptr

无符号整型,用于寄存一个指针

定义变量

// 申明一个变量
var identifier type
// 能够一次申明多个变量
var identifier1, identifier2 type
// 依据值自行断定变量类型
var v_name = value
// 简短模式 省略 var, 留神 := 左侧如果没有申明新的变量
v_name := value

定义常量

// 申明一个常量
const identifier [type] = value
// 显式类型定义
const b string = "abc"
// 隐式类型定义
const b = "abc"
// 多个雷同类型的申明(隐式类型定义)const c_name1, c_name2 = value1, value2

iota

iota,非凡常量,能够认为是一个能够被编译器批改的常量

iota 在 const 关键字呈现时将被重置为 0(const 外部的第一行之前),const 中每新增一行常量申明将使 iota 计数一次 (iota 可了解为 const 语句块中的行索引)。

iota 能够被用作枚举值

package main

import "fmt"

func main() {
    const (
        a = iota   //0
        b          //1
        c          //2
        d = "ha"   // 独立值,iota += 1
        e          //"ha"   iota += 1
        f = 100    //iota +=1
        g          //100  iota +=1
        h = iota   //7, 复原计数
        i          //8
    )
    fmt.Println(a,b,c,d,e,f,g,h,i)
}

运行后果

0 1 2 ha ha 100 100 7 8

第一个 iota 等于 0,每当 iota 在新的一行被应用时,它的值都会主动加 1

条件管制语句

if & if else

package main

import "fmt"

func main() {
    var a = 12
    if a > 10 {fmt.Println("a>10")
    } else {fmt.Println("a<=10")
    }
}

运行后果

a>10

switch

package main

import "fmt"

func main() {
    var a = 12
    switch a {
    case 1:
        fmt.Println(1)
    case 2:
        fmt.Println(2)
    case 12:
        fmt.Println(12)
    default:
        fmt.Println(a)
    }
}

运行后果

12

应用 fallthrough 会强制执行前面的 case 语句,fallthrough 不会判断下一条 case 的表达式后果是否为 true

package main

import "fmt"

func main() {
    var a = 1
    switch a {
    case 1:
        fmt.Println(1)
        fallthrough
    case 2:
        fmt.Println(2)
    case 12:
        fmt.Println(12)
    default:
        fmt.Println(a)
    }
}

运行后果

1
2

select

select 是 Go 中的一个控制结构,相似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接管。

select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。

package main

import "fmt"

func main() {
    var c1, c2, c3 chan int
    var i1, i2 int
    select {
    case i1 = <-c1:
        fmt.Printf("received", i1, "from c1\n")
    case c2 <- i2:
        fmt.Printf("sent", i2, "to c2\n")
    case i3, ok := <-c3:
        if ok {fmt.Printf("received", i3, "from c3\n")
        } else {fmt.Printf("c3 is closed\n")
        }
    default:
        fmt.Printf("no communication\n")
    }
}

运行后果

no communication

循环管制语句

for

package main

import "fmt"

func main() {
    for i := 1; i < 10; i++ {fmt.Println(i)
    }
}
package main

import "fmt"

func main() {
    var i = 1
    for i < 10 {fmt.Println(i)
        i++
    }
}

运行后果

1
2
3
4
5
6
7
8
9

死循环

for {}

函数

package main

import "fmt"

func main() {test(1)
}
func test(i int) int {
    for i < 10 {fmt.Println(i)
        i++
    }
    return i
}

运行后果

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {i := test(1, 9)
    fmt.Println("最大值为:", i)
}
func test(i, j int) int {
    if i > j {return i} else {return j}
}

运行后果

 最大值为:9

函数返回多个值

package main

import "fmt"

func main() {s, s2 := test("hello", "go")
    fmt.Println(s, s2)
}
func test(i, j string) (string, string) {return i, j}

运行后果

hello go

值传递 和 援用传递

package main

import "fmt"

func main() {
    var a = 3
    var b = 4
    fmt.Println("值传递运行前 a =", a, "b=", b)
    test1(a, b)
    fmt.Println("值传递运行后 a =", a, "b=", b)
    fmt.Println("===============================================")
    var i = 1
    var j = 2
    fmt.Println("援用传递运行前 i =", i, "j=", j)
    test2(&i, &j)
    fmt.Println("援用传递运行后 i =", i, "j=", j)
}

// 值传递
func test1(i, j int) (int, int) {
    var temp int
    temp = i
    i = j
    j = temp
    return i, j
}

// 援用传递
func test2(i, j *int) (int, int) {
    var temp int
    temp = *i
    *i = *j
    *j = temp
    return *i, *j
}

运行后果

 值传递运行前 a = 3 b= 4
值传递运行后 a = 3 b= 4
===============================================
援用传递运行前 i = 1 j= 2
援用传递运行后 i = 2 j= 1

函数作为实参

package main

import "fmt"

func main() {funcA := func(a int) int {return a}
    fmt.Println(funcA(12))
}

运行后果

12

闭包

Go 语言反对匿名函数,可作为闭包。匿名函数是一个 ” 内联 ” 语句或表达式。匿名函数的优越性在于能够间接应用函数内的变量,不用申明。

package main

import "fmt"

func main() {next := getSequence()
    fmt.Println(next())
    fmt.Println(next())
    fmt.Println(next())
}

func getSequence() func() int {
    a := 1
    return func() int {
        a++
        return a
    }
}

运行后果

2
3
4

办法

Go 语言中同时有函数和办法。一个办法就是一个蕴含了接受者的函数,接受者能够是命名类型或者构造体类型的一个值或者是一个指针。所有给定类型的办法属于该类型的办法集

package main

import "fmt"

type Circle struct {radius float64}

func (circle Circle) getPerimeter() float64 {return 3.14 * circle.radius * 2}
func main() {
    var circle Circle
    circle.radius = 10
    fmt.Println(circle.getPerimeter())
}

运行后果

62.800000000000004

变量作用域

Go 语言中变量能够在三个中央申明:

  • 函数内定义的变量称为局部变量
  • 函数外定义的变量称为全局变量
  • 函数定义中的变量称为形式参数
package main

import "fmt"

// 全局变量
var a = 1

func main() {
    // 局部变量
    var b = 2
    test(a)
    test(b)
}

// 形式参数
func test(a int) {fmt.Println(a)
}

数组

数组是具备雷同惟一类型的一组已编号且长度固定的数据项序列,这种类型能够是任意的原始类型例如整形、字符串或者自定义类型

申明数组

// 模式
var variable_name [SIZE] variable_type
// 举例
var balance [10] float32

初始化数组

// 初始化一个长度为 5 的 float32 数组
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
// 如果疏忽 [] 中的数字不设置数组大小
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
balance[6] = 60.0

拜访数组元素

var a float32 = balance[5]

指针

一个指针变量指向了一个值的内存地址

申明指针

// 模式
var var_name *var-type
// 举例
var ip *int        /* 指向整型 */
var fp *float32    /* 指向浮点型 */
package main

import "fmt"

func main() {
    var a int = 20 /* 申明理论变量 */
    var ip *int    /* 申明指针变量 */

    ip = &a /* 指针变量的存储地址 */

    fmt.Printf("a 变量的地址是: %x\n", &a)

    /* 指针变量的存储地址 */
    fmt.Printf("ip 变量贮存的指针地址: %x\n", ip)

    /* 应用指针拜访值 */
    fmt.Printf("*ip 变量的值: %d\n", *ip)
}

运行后果

a 变量的地址是: c00000a0b0
ip 变量贮存的指针地址: c00000a0b0
*ip 变量的值: 20

空指针

当一个指针被定义后没有调配到任何变量时,它的值为 nil。

nil 指针也称为空指针。一个指针变量通常缩写为 ptr。

package main

import "fmt"

func main() {
    var ip *int /* 申明指针变量 */

    /* 指针变量的存储地址 */
    fmt.Printf("ip 的值为: %x\n", ip)
}

运行后果

ip 的值为: 0

空指针判断

if(ptr != nil)     /* ptr 不是空指针 */
if(ptr == nil)    /* ptr 是空指针 */

指针数组

package main

import "fmt"

func main() {a := []int{10, 100, 200}
    // 遍历数组
    for i := 0; i < len(a); i++ {fmt.Printf("a[%d] = %d\n", i, a[i])
    }
    fmt.Println("==================================")
    // 有一种状况,咱们可能须要保留数组,这样咱们就须要应用到指针。// 以下申明了整型指针数组:var ptr [3]*int
    for i := 0; i < len(a); i++ {
        /* 整数地址赋值给指针数组 */
        ptr[i] = &a[i]
    }
    for i := 0; i < len(ptr); i++ {fmt.Printf("a[%d] = %d\n", i, *ptr[i])
    }
}

运行后果

a[0] = 10
a[1] = 100
a[2] = 200
==================================
a[0] = 10
a[1] = 100
a[2] = 200

指向指针的指针

如果一个指针变量寄存的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。
指向指针的指针变量申明格局

var ptr **int;
package main

import "fmt"

func main() {
    var a int
    var ptr *int
    var pptr **int

    a = 3000

    /* 指针 ptr 地址 */
    ptr = &a

    /* 指向指针 ptr 地址 */
    pptr = &ptr

    /* 获取 pptr 的值 */
    fmt.Printf("变量 a = %d\n", a)
    fmt.Printf("指针变量 *ptr = %d\n", *ptr)
    fmt.Printf("指向指针的指针变量 **pptr = %d\n", **pptr)
}

运行后果

 变量 a = 3000
指针变量 *ptr = 3000
指向指针的指针变量 **pptr = 3000

指针作为函数参数

package main

import "fmt"

func main() {
    var a = 3
    var b = 4
    fmt.Println("值传递运行前 a =", a, "b=", b)
    test1(a, b)
    fmt.Println("值传递运行后 a =", a, "b=", b)
    fmt.Println("===============================================")
    var i = 1
    var j = 2
    fmt.Println("援用传递运行前 i =", i, "j=", j)
    test2(&i, &j)
    fmt.Println("援用传递运行后 i =", i, "j=", j)
}

// 值传递
func test1(i, j int) (int, int) {
    var temp int
    temp = i
    i = j
    j = temp
    return i, j
}

// 援用传递
func test2(i, j *int) (int, int) {
    var temp int
    temp = *i
    *i = *j
    *j = temp
    return *i, *j
}

运行后果

 值传递运行前 a = 3 b= 4
值传递运行后 a = 3 b= 4
===============================================
援用传递运行前 i = 1 j= 2
援用传递运行后 i = 2 j= 1

构造体

构造体是由一系列具备雷同类型或不同类型的数据形成的数据汇合。

构造体示意一项记录,比方保留图书馆的书籍记录,每本书有以下属性:

Title:题目
Author:作者
Subject:学科
ID:书籍 ID

定义

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

一旦定义了构造体类型,它就能用于变量的申明,语法格局如下:

variable_name := structure_variable_type {value1, value2...valuen}
或
variable_name := structure_variable_type {key1: value1, key2: value2..., keyn: valuen}
package main

import "fmt"

// 一、定义构造体
type Books struct {
    title   string
    author  string
    subject string
    book_id int
}

func main() {

    // 创立一个新的构造体
    fmt.Println(Books{"Go 语言", "Google", "Go 语言教程", 6495407})

    // 也能够应用 key => value 格局
    fmt.Println(Books{title: "Go 语言", author: "Google", subject: "Go 语言教程", book_id: 6495407})

    // 疏忽的字段为 0 或 空
    fmt.Println(Books{title: "Go 语言", author: "Google"})

    fmt.Println("=========================")

    // 二、拜访构造体成员
    /* book 2 形容 */
    var Book2 Books
    Book2.title = "Python 教程"
    Book2.author = "Python"
    Book2.subject = "Python 语言教程"
    Book2.book_id = 6495700
    /* 打印 Book2 信息 */
    fmt.Printf("Book 2 title : %s\n", Book2.title)
    fmt.Printf("Book 2 author : %s\n", Book2.author)
    fmt.Printf("Book 2 subject : %s\n", Book2.subject)
    fmt.Printf("Book 2 book_id : %d\n", Book2.book_id)
    fmt.Println("=========================")
    // 三、构造体作为函数参数
    printBook(Book2)
    fmt.Println("=========================")
    // 四、构造体指针
    printBook2(&Book2)
}

func printBook(book Books) {fmt.Printf("Book title : %s\n", book.title)
    fmt.Printf("Book author : %s\n", book.author)
    fmt.Printf("Book subject : %s\n", book.subject)
    fmt.Printf("Book book_id : %d\n", book.book_id)
}

func printBook2(book *Books) {fmt.Printf("Book title : %s\n", book.title)
    fmt.Printf("Book author : %s\n", book.author)
    fmt.Printf("Book subject : %s\n", book.subject)
    fmt.Printf("Book book_id : %d\n", book.book_id)
}

运行后果

{Go 语言 Google Go 语言教程 6495407}
{Go 语言 Google Go 语言教程 6495407}
{Go 语言 Google  0}
=========================
Book 2 title : Python 教程
Book 2 author : Python
Book 2 subject : Python 语言教程
Book 2 book_id : 6495700
=========================
Book title : Python 教程
Book author : Python
Book subject : Python 语言教程
Book book_id : 6495700
=========================
Book title : Python 教程
Book author : Python
Book subject : Python 语言教程
Book book_id : 6495700

切片 (Slice)

Go 数组的长度不可扭转, 与数组相比切片的长度是不固定的,能够追加元素,在追加时可能使切片的容量增大。

定义切片

  1. var identifier []type
  2. var slice1 []type = make([]type, len) 或者简写 slice1 := make([]type, len) 指定长度
  3. make([]T, length, capacity) 指定容量
package main

import "fmt"

func main() {
    // 切片初始化
    var slice = []int{1, 2, 3}
    // 从下标 startIndex 到 endIndex-1 下的元素 切片截取
    fmt.Println(slice[0:2])
    fmt.Println(slice[:2])
    fmt.Println(slice[0:])
    fmt.Println("=======================")
    // len() 和 cap() 函数
    fmt.Printf("len=%d cap=%d slice=%v\n", len(slice), cap(slice), slice)
    fmt.Println("=======================")
    // 空切片
    var numbers []int

    fmt.Printf("len=%d cap=%d slice=%v\n", len(numbers), cap(numbers), numbers)

    if numbers == nil {fmt.Printf("切片是空的 \n")
    }
    fmt.Println("=======================")
    // append() 和 copy() 函数
    var numbers1 []int
    // append() 追加
    numbers1 = append(numbers1, 1)
    numbers1 = append(numbers1, 2, 3, 4)
    fmt.Printf("len=%d cap=%d slice=%v\n", len(numbers1), cap(numbers1), numbers1)
    fmt.Println("=======================")
    // copy() 复制
    /* 创立切片 numbers2 是之前切片的两倍容量 */
    numbers2 := make([]int, len(numbers1), (cap(numbers1))*2)
    /* 拷贝 numbers1 的内容到 numbers2 */
    copy(numbers2, numbers1)
    fmt.Printf("len=%d cap=%d slice=%v\n", len(numbers2), cap(numbers2), numbers2)
}

运行后果

[1 2]
[1 2]
[1 2 3]
=======================
len=3 cap=3 slice=[1 2 3]
=======================
len=0 cap=0 slice=[]
切片是空的
=======================
len=4 cap=4 slice=[1 2 3 4]
=======================
len=4 cap=8 slice=[1 2 3 4]

范畴 (Range)

package main

import "fmt"

func main() {
    // 这是咱们应用 range 去求一个 slice 的和。应用数组跟这个很相似
    nums := []int{2, 3, 4}
    sum := 0
    for _, num := range nums {sum += num}
    fmt.Println("sum:", sum)
    // 在数组上应用 range 将传入 index 和值两个变量。下面那个例子咱们不须要应用该元素的序号,所以咱们应用空白符 "_" 省略了。有时侯咱们的确须要晓得它的索引。for i, num := range nums {
        if num == 3 {fmt.Println("index:", i)
        }
    }
    //range 也能够用在 map 的键值对上。kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {fmt.Printf("%s -> %s\n", k, v)
    }
    //range 也能够用来枚举 Unicode 字符串。第一个参数是字符的索引,第二个是字符(Unicode 的值)自身。for i, c := range "go" {fmt.Println(i, c)
    }
}

运行后果

sum: 9
index: 1
a -> apple
b -> banana
0 103
1 111

Map(汇合)

Map 是一种无序的键值对的汇合。Map 最重要的一点是通过 key 来疾速检索数据,key 相似于索引,指向数据的值。

定义汇合

/* 申明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type

/* 应用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)

如果不初始化 map,那么就会创立一个 nil map。nil map 不能用来寄存键值对

package main

import "fmt"

func main() {var countryCapitalMap = make(map[string]string)

    /* map 插入 key - value 对, 各个国家对应的首都 */
    countryCapitalMap["France"] = "巴黎"
    countryCapitalMap["Italy"] = "罗马"
    countryCapitalMap["Japan"] = "东京"
    countryCapitalMap["India"] = "新德里"

    /* 应用键输入地图值 */
    for country := range countryCapitalMap {fmt.Println(country, "首都是", countryCapitalMap[country])
    }

    /* 查看元素在汇合中是否存在 */
    capital, ok := countryCapitalMap["American"] /* 如果确定是实在的, 则存在, 否则不存在 */
    /*fmt.Println(capital) */
    /*fmt.Println(ok) */
    if ok {fmt.Println("American 的首都是", capital)
    } else {fmt.Println("American 的首都不存在")
    }

    fmt.Println("========================")
    // delete() 函数
    for country := range countryCapitalMap {fmt.Println(country, "首都是", countryCapitalMap[country])
    }
    // 删除元素
    delete(countryCapitalMap, "France")
    fmt.Println("法国条目被删除")
    for country := range countryCapitalMap {fmt.Println(country, "首都是", countryCapitalMap[country])
    }
}

运行后果

France 首都是 巴黎
Italy 首都是 罗马
Japan 首都是 东京
India  首都是 新德里
American 的首都不存在
========================
India  首都是 新德里
France 首都是 巴黎
Italy 首都是 罗马
Japan 首都是 东京
法国条目被删除
Japan 首都是 东京
India  首都是 新德里
Italy 首都是 罗马 

递归函数

递归,就是在运行的过程中调用本人

阶乘

package main

import "fmt"

func main() {
    var i int = 15
    fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))
}

func Factorial(n uint64) (result uint64) {
    if n > 0 {result = n * Factorial(n-1)
        return result
    }
    return 1
}

运行后果

15 的阶乘是 1307674368000

斐波那契数列

package main

import "fmt"

func main() {
    var i int
    for i = 0; i < 10; i++ {fmt.Printf("%d\t", fibonacci(i))
    }
}

func fibonacci(n int) int {
    if n < 2 {return n}
    return fibonacci(n-2) + fibonacci(n-1)
}

运行后果

0    1    1    2    3    5    8    13    21    34

类型转换

类型转换用于将一种数据类型的变量转换为另外一种类型的变量。

type_name(expression)
package main

import "fmt"

func main() {
    var sum int = 17
    var count int = 5
    var mean float32

    mean = float32(sum) / float32(count)
    fmt.Printf("mean 的值为: %f\n", mean)
}

运行后果

mean 的值为: 3.400000

接口

package main

import "fmt"

type Phone interface {call()
}

type NokiaPhone struct {
}
type IPhone struct {
}

func main() {n := new(NokiaPhone)
    n.call()
    i := new(IPhone)
    i.call()}

func (NokiaPhone) call() {fmt.Println("nokiaPhone")
}

func (IPhone) call() {fmt.Println("IPhone")
}

运行后果

nokiaPhone
IPhone

错误处理

Go 语言通过内置的谬误接口提供了非常简单的错误处理机制。

error 类型是一个接口类型,这是它的定义

type error interface {Error() string
}

咱们能够在编码中通过实现 error 接口类型来生成错误信息。

函数通常在最初的返回值中返回错误信息。应用 errors.New 可返回一个错误信息

package main

import (
    "errors"
    "fmt"
)

func main() {_, err := Sqrt(-1)

    if err != nil {fmt.Println(err)
    }
}

func Sqrt(f float64) (float64, error) {
    if f < 0 {return 0, errors.New("math: square root of negative number")
    }
    // 实现
    return f, nil
}

运行后果

math: square root of negative number

并发

Go 语言反对并发,咱们只须要通过 go 关键字来开启 goroutine 即可。

goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行治理的。

goroutine 语法格局:

go 函数名 (参数列表)

Go 容许应用 go 语句开启一个新的运行期线程,即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。同一个程序中的所有 goroutine 共享同一个地址空间。

package main

import (
    "fmt"
    "time"
)

func main() {go say("world")
    say("hello")
}

func say(s string) {
    for i := 0; i < 5; i++ {time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

运行后果

hello
world
world
hello
hello
world
world
hello
hello

通道(channel)

通道(channel)是用来传递数据的一个数据结构。

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通信。操作符 <- 用于指定通道的方向,发送或接管。如果未指定方向,则为双向通道。
申明一个通道,通道在应用前必须先创立:

ch := make(chan int)

留神:默认状况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。

package main

import ("fmt")

func main() {s := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y := <-c, <-c // 从通道 c 中接管

    fmt.Println(x, y, x+y)
}

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {sum += v}
    c <- sum // 把 sum 发送到通道 c
}

运行后果

-5 17 12

通道缓冲区

通道能够设置缓冲区,通过 make 的第二个参数指定缓冲区大小:

ch := make(chan int, 100)

带缓冲区的通道容许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据能够放在缓冲区外面,能够期待接收端去获取数据,而不是立即须要接收端去获取数据。

不过因为缓冲区的大小是无限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无奈再发送数据了。

留神:如果通道不带缓冲,发送方会阻塞直到接管方从通道中接管了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着须要期待直到某个接管方获取到一个值。接管方在有值能够接管之前会始终阻塞。

package main

import ("fmt")

func main() {
    // 这里咱们定义了一个能够存储整数类型的带缓冲通道
    // 缓冲区大小为 2
    ch := make(chan int, 2)

    // 因为 ch 是带缓冲的通道,咱们能够同时发送两个数据
    // 而不必立即须要去同步读取数据
    ch <- 1
    ch <- 2

    // 获取这两个数据
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

运行后果

1
2

遍历通道与敞开通道

Go 通过 range 关键字来实现遍历读取到的数据,相似于与数组或切片。格局如下:

v, ok := <-ch
package main

import ("fmt")

func main() {c := make(chan int, 10)
    go fibonacci(cap(c), c)
    // range 函数遍历每个从通道接管到的数据,因为 c 在发送完 10 个
    // 数据之后就敞开了通道,所以这里咱们 range 函数在接管到 10 个数据
    // 之后就完结了。如果下面的 c 通道不敞开,那么 range 函数就不
    // 会完结,从而在接管第 11 个数据的时候就阻塞了。for i := range c {fmt.Println(i)
    }
}

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x+y
    }
    // 敞开通道
    close(c)
}

运行后果

0
1
1
2
3
5
8
13
21
34
正文完
 0