Golang 基礎整理 這一篇就夠了

2021-01-14 科技貓
第一個golang程序

package main

import "fmt"

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

基本數據類型

布爾型( true 或者 false)

數字類型( 整型 int 和 浮點型 float32、float64 )

字符串類型( 字符串就是一串固定長度的字符連接起來的字符序列 )

派生類型:

指針類型(Pointer)

數組類型

結構化類型(struct)

Channel 類型

函數類型

切片類型

接口類型(interface)

Map 類型

數字類型

整形

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)

浮點型

float32
32位浮點型數

float64
64位浮點型數

complex64
32 位實數和虛數

complex128
64 位實數和虛數

其他數字類型

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 數組的長度不可改變,與數組相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。

定義切片

var identifier []type

var slice1 []type = make([]type, len) 或者簡寫 slice1 := make([]type, len) 指定長度

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

相關焦點

  • api框架 web 最好的go_golang api框架 - CSDN
    曾經我以為Python世界裡的框架已經夠多了,後來發現相比golang簡直小巫見大巫。golang提供的net/http庫已經很好了,對於http的協議的實現非常好,基於此再造框架,也不會是難事,因此生態中出現了很多框架。既然構造框架的門檻變低了,那麼低門檻同樣也會帶來質量參差不齊的框架。
  • MBA英語答題看這一篇就夠了!
    原標題:MBA英語答題看這一篇就夠了!  目前,MBA聯考已進入了最後的衝刺複習階段,每個科目都要在全面回顧的基礎上,及時調整,查漏補缺,提升學習效率。除此之外,最重要的便是掌握一些考試時能夠運用的答題技巧和策略,更有針對性地進行備考複習。
  • AI足球大數據爬蟲分析(golang)
    程序採用golang開發,項目模塊化結構清晰完整,非常容易入手並進行二次開發分析. AI球探為程序全自動處理,全程無人為參與幹預足球分析預測程序. 避免了人為分析的主觀性及不穩定因素. 程序根據各大指數多維度數據,結合作者多年足球分析經驗,精雕細琢, 集天地之靈氣,汲日月之精華,歷時七七四十九天,經Bug九九八十一個,編碼而成.
  • 專題整理|關於「後真相」,只看這一篇就夠了
    (圖片來源:察網)其實「後真相」一詞由來已久: 1992年,美國《國家》雜誌上發表了一篇關於海灣戰爭的文章由此推理可得,我們的現實是由權力所構建的,這使得現實本身就是「可塑」的,同時又是值得被抵抗的。這就為「後真相」的發生提供了理論上的合理性支持。 從現實的角度講,由於全球化和網際網路帶來的全球普遍連結,社會和經濟的「不可控」越發強烈的被大眾所感知,而政府和專家在其中仿佛沒有時間和能力對這種風險予以控制,這就使得原先的權威機構失去了其權威。這為「後真相」中輿論中對於原先事實來源的普遍拋棄提供了現實基礎。
  • 關於社群玩兒法,這一篇就夠了!
    關於社群玩兒法,這一篇就夠了!以下是其分享的內容整理: 一、為什麼這個時代大家都在玩兒社群?我覺得玩社群的原因主要有三個:第一,可以解決流量問題。第二,可以解決品牌問題。第三,是危機給我們帶來的新機會。怎麼理解這三方面的原因呢?
  • 看圖學NumPy:掌握n維數組基礎知識點,看這一篇就夠了
    曉查 編譯整理量子位 報導 | 公眾號 QbitAINumPy是Python的最重要的擴展程序庫之一,也是入門機器學習編程的必備工具。然而對初學者來說,NumPy的大量運算方法非常難記。
  • 光纖基礎知識介紹,看懂這一篇就夠!
    弱電監控系統中,當鏈路傳輸距離超過100米後,我們就會考慮使用光纖傳輸,光纖具有抗幹擾能力強,傳輸距離遠,帶寬大等優勢,今天我們就來一起聊聊光纖的基礎知識!FC光纖連接器這類光纖連接器外部加強方式採用金屬套,緊固方式為螺絲扣,一般在ODF配線架上用的比較多。
  • 高中生物常用30個知識點匯總 有這一篇就夠了
    高中生物常用30個知識點匯總 有這一篇就夠了針對高中生物有哪些高效的學習方法呢?其中有一條很值得同學們參考,那就是把知識點匯總在一起常看、常背。為此小編整理了高中生物常用知識點供大家參考。高中生物常用知識點1.細胞是生物體的結構和功能的基本單位;細胞是一切動植物結構的基本單位。
  • 最基礎的英語學習教材該怎麼用?看這一篇就夠了
    其實關於高頻詞的學習我分享過蠻多了,畢竟這是英文基礎,國外孩子也繞不開,而我家孩子在這一方面的學習也是用了一些時日、費了一番功夫的,所以我也積累了不少經驗心得。但對於高頻詞的學習教材,沒有進行過匯總詳解,今天就想著特意來給大家好好解析一番。那麼學習高頻詞的教材通常都有哪些呢?
  • 考研英語複習攻略,看這一篇就夠了!
    翻轉英語君來給大家講一講這其中的奧秘。#考研英語#首先,考研英語分為英語一和英語二。大部分的學碩是考英語一,專碩是考英語二,英語一總體上比英語二難。考研英語一與英語二均為閉卷筆試,滿分為100分,考試時間為3個小時。在準備考研英語之前,同學們得清楚自己考的是考研一還是考研二。要弄清楚這個問題很簡單。大家可以去院校官網查看自己所報專業需要考試的科目,就知道考英一還是英二了。
  • 小學語文基礎:諺語+俗語+歇後語,一篇全匯總,留給孩子積累!
    小學語文基礎:諺語+俗語+歇後語,一篇全匯總,留給孩子慢慢學,作文也能有素材!在我們日常生活中,諺語、俗語、歇後語隨處可見,如「光說不練假把式」、「百聞不如一見」、「麻雀雖小——五臟俱全」、「三十六計——走為上計」等等,這些語言約定俗成,通俗易懂,傳遞著古人的智慧。
  • 圖解golang裡面的讀寫鎖實現與核心原理分析了解程式語言背後設計
    基礎築基1.1 讀寫鎖的特點讀寫鎖區別與互斥鎖的主要區別就是讀鎖之間是共享的,多個goroutine可以同時加讀鎖,但是寫鎖與寫鎖、寫鎖與讀鎖之間則是互斥的1.2 寫鎖飢餓問題因為讀鎖是共享的,所以如果當前已經有讀鎖,那後續goroutine繼續加讀鎖正常情況下是可以加鎖成功,但是如果一直有讀鎖進行加鎖,那嘗試加寫鎖的goroutine則可能會長期獲取不到鎖
  • 乾貨分享| 理解數據關係看這一篇就夠了
    上一篇文章已經探討過如何建立數據分析思路素養,如果對定性數據和定量數據的鑑別還是一知半解,推薦先區分清兩者再往下閱讀,畢竟數據類型是一切研究的基礎。或者參考SPSSAU中的幫助手冊都有詳細說明。上表中列出常見的關係研究涉及方法;相關分析是比較基礎的關係研究,以及可以使用散點圖直觀展示數據關係情況。回歸分析研究X對於Y的影響關係,並且Y為定量;同時還有兩個方法即逐步回歸,分層回歸;其實質上均是回歸;逐步回歸是指讓軟體自己找出對於Y有影響的X;分層回歸是指一次性運行多個回歸。
  • Q-PCR 計算看這一篇就夠了丨實驗時間
    Q-PCR 計算看這一篇就夠了【你可能還喜歡】
  • 今日頭條oCPC,看這一篇就夠了
    今日頭條oCPC,看這一篇就夠了 !轉化出價這個話題很早就想跟各位嘮嘮了,很多信息流平臺目前最主流的出價方式就是 oCPC ( Optimized Cost Per Click ,以目標轉化為優化方式的點擊出價)。
  • 機械製圖基礎知識,只用看這一篇就夠了!
    其使用方法:(1)至少保證4個點(或4個以上的點)與曲線板的邊緣相吻合,才能連接這4個點(或4個以上的點)。(2)兩段之間應有重複。二、常用的繪圖用品1、鉛筆:鉛筆分:硬、中、軟三種。其標號有:6H、5H、4H、3H、2H、H、HB、B、2B、3B、4B、5B、6B共13種。
  • 鞏固基礎,複習有這一份就夠了!
    很多同學在進入初中學習物理的時候,總是去攻克難題,殊不知這是錯誤的做法,想要在物理這一門科目上取得優異的成績,那麼就要掌握住物理的基礎知識,畢竟初中的物理不會很難,大多數都是由基礎的題目構成的,難題所佔整個題目的比重不會太多,所以想要物理考得好,那麼初中物理的必考知識點就是需要掌握住的內容了
  • 股市指南針:炒股,看這一篇就夠了!
    D、買賣法則1、價格窄幅整理,而成交量呈逐波遞減或者溫和放大、均線形成黃金交*或者一致向上,或者均線粘合、多頭排列,且周K線也出現類似的圖形,可買入。2、均線空頭排列且成交量分布不規則,量大而漲幅小,上影線長,高位震蕩劇烈,價格屢創新低,可作為賣出依據。
  • 假設檢驗的初步了解,這一篇就夠了
    今天為大家帶來一篇關於假設檢驗的初步了解的文章。可能有些地方描述不恰當。1假設檢驗時代發展首先對於假設檢驗,科學技術的不斷創新,不僅促進了社會的進步,還改善了人們的生活水平。與此同時,社會生活中待檢驗的事件日漸增多。為此,當我們去檢驗這些事件的真實性時,就要用到統計論斷中的假設檢驗。當前已廣泛應用於醫學、氣象、地理等領域,比如,醫學的製藥行業。
  • 數據產品必備技術知識:數據倉庫入門,看這這一篇就夠了
    我也一直零零散散的積累這方面的知識,這兩天梳理了下,形成下文,希望對大家有所幫助,非專業數倉開發人員,如有不準確的地方,還望大家指正。文章結構一、數據倉庫是什麼二、數據倉庫有什麼特點三、為什麼搭建數據倉庫四、數據倉庫結構五、ETL六、數據集市七、ODS八、元數據一、數據倉庫是什麼可以理解為:面向分析的存儲系統。