go语言基础编程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013862108/article/details/86558883

Go语言的项目 / Go语言有哪些应用

服务器编程:如处理日志, 数据打包,虚拟机处理,文件系统等;

分布式系统, 数据库代理器,中间件等;

网络编程: Web应用, API应用等;

云平台,目前云平台逐步采用Go实现

Docker

Kubernetes  : 简称K8s   ;是Google开源的一个容器编排引擎,它支持自动化部署,大规模可伸缩,应用容器化管理。

在生产环境中部署一个应用程序时,通常要部署该应用的多个实例以便对应用请求进行负载均衡。

Caddy :  (或称Caddy Web)是一个开源的,使用 Go 编写,支持 HTTP/2 的 Web 服务端

CockroachDB: 目标是打造一个开源,可伸缩, 跨地域复制且兼容事务的ACID特性的分布式数据库。

Go语言的设计初衷?

针对其他语言的痛点进行设计

并加入并发编程

为大数据,微服务,并发而生的通用编程语言。

Go语言特别之处?

没有对象, 没有继承,多态,没有泛型,没有try/catch

有接口,函数式编程, CSP并发模型(goroutine + channel)

学习Go语言很简单,因为语法简单

要调整三观

Go语言的特点?

内置runtime(作用:性能监控,GC等);

丰富的标准库,强大的网络库;

内置强大的工具(gofmt),跨平台编译,内嵌C支持;

Go语言 注释:

注释形式 :

1.    //  单行注释

2.   /*    */   多行注释

包:/封装 /  package  / import  

package 是最基本的分发单位和 工程管理中依赖关系的体现;

每个Go语言源码文件开头都拥有一个package声明,表示源码文件所属代码包;

要生成Go语言 可执行程序,必须要有main的package包,且必须在该包下有main()函数;

同一个路径下只能存在一个package,一个package可以拆成多个源文件组成;

import:

import 语句可以导入源代码文件所依赖的package包;

不得导入源代码中没有用到的package,否则go语言编译器会报编译错误;

import语法主要有两种格式:

//推荐这种写法
import (
	"package1"
	"package2"
	"package3"
)

import "package1"
import "package2"
import "package3"

如果一个main导入其他包,包将被顺序导入;

如果导入的包中依赖其他包(包B),会首先导入B 包,然后初始化B包中常量和变量,最后如果B包中有init,会自动执行init();

所有包导入完成后才会对main中常量和变量进行初始化,然后执行main中init函数(如果存在),最后执行main()函数;

如果一个包被导入多次则该包只会被导入一次;

import 别名,路径, “."  , "_" 的使用说明;

别名操作的含义是:将导入的包命名为另一个容易记忆的别名;

点(.) 操作的含义是: 点(.)标示的包导入后,调用该包中函数时可以省略前缀包名;

下划线(_)操作的含义是:导入该包,但不导入整个包,而是执行该包中的init函数,因此无法通过包名来调用包中其他函数。使用下划线(_)操作往往是为了注册包里的引擎,让外部可以方便地使用;

Go 变量, 函数可见性规则 ?

想 public  包里边变量或函数名字 首字母大写

想 private 包里边变量或函数名字 首字母大写

名字一般使用CamelCase (驼峰

Go 中 保留关键字 25个 ?

break   default  func   interface  select   case  defer  go  map  struct chan  else  goto  package  switch  const  fallthrough

if range   type continue  for  import  return var

Go中36个预定的标识符,其包括基础数据类型和系统内嵌函数:

append bool byte  cap  close  complex complex64 complex128 unit16  copy  false  float32  float64 imag int int8 int16

uint32 int32 int64 iota len make new nil panic uint64  print println real  recover  string TRUE uint uint8 uintprt

变量定义:

使用var 关键字

var a,b,c bool

var s1,s2 string = “hello”, “world”

可放在函数内,或直接放在包内

可使用var() 集中定义变量

全局变量的声明必须使用var 关键词,局部变量则可以省略;

特殊变量: 下划线 "_"

使用 := 定义变量

a,b,i,s1,s2 := true, false, 3, “hello”, “world”

只能在函数内使用

内建变量类型

bool, string

int, int8, int16, int32, int64, 

uint, uint8, uint16, uint32, uint64,

uintptr

byte, rune

float32, float64 complex64, complex128

字符串类型string ,编码统一为“UTF-8"

派生类型:

指针类型(Pointer)

数组类型

结构化类型(struct)

Channel类型(chan)

函数类型(func)

切片类型(slice)

接口类型(interface)

Map类型(map)

类型的默认值:

值类型的默认值为0, 布尔型默认值为false,  string默认值为空字符串

复数概念:todo;

强制类型转换;go 中没有隐式转换,类型转换必须是显式的;

类型转换只能发生在两种兼容的类型之间;

类型转换格式 : 【目标类型】(【需要转换的变量】)

例子:

var a,b int =3, 4

var c int = math.Sqrt(a*a + b*b)   // 这样会报错,类型不匹配

var c int = int(math.Sqrt(float64(a*a+ b*b))   //这样才行;必须手动强制转换

常量定义

从定义形式上可分为显式和隐式:

显式: const a  int  = 3

隐式 : const a = 3

const filename = “abc.txt”

const a,b = 3,4

常量可以由内置表达式产生如: len(), unsafe.Sizeof() 等

常量目前 只支持布尔型,数字型(整数,浮点,复数), 字符串型

特殊常量 iota

iota 在const 关键字出现时被重置为0;

const中每新增一行常量声明 将使iota计数一次;

iota 常见使用法:

1. 跳值使用法;

2. 插入使用法;

3. 表达式隐式使用法

4. 单行使用法;

算数运算符:

+ -  *   /  %   ++   --

注意自增,自减  只有后置操作这一种: A++

关系运算符:

==   !=   >   <  >=  <=

逻辑运算符

&&   ||    !

按位运算符

&   |   ^  <<   >>

赋值运算符

=  +=   -=   *=   /=  %=   <<=   >>=   &=   ^=   |=

流程控制语句:

if 条件控制语句

注意: if的条件里不需要括号

func bounded(v int) int{
	if v > 100{
		return 100
	} else if v < 0{
		return 0
	} else {
		return v
	}
}

switch 流程控制语句

switch 会自动break, 除非使用fallthrough

func eval(a,b int, op string) int {
	var result int
	switch op {
	case "+":
		result = a + b;
	case "-":
		result = a - b;
	case "*":
		result = a * b;
	case "/":
		result = a / b;
	default:
		panic("unsupported operator:" + op)
	}
	return result
}

// switch 后可以没有表达式

// switch 后可以没有表达式
func grade(score int) string{
	switch {
	case score < 60:
		return "F"
	case score < 80:
		return "C"
	case score < 90:
		return "B"
	default :
		return "A"
	}
}

for 循环控制语句

//省略初始条件,相等于while

//省略初始条件,相等于while
func convertToBin(v int) string{
	result := ""
	for ; v > 0; v /= 2{
		result = strconv.Itoa(v%2) + result
	}

	return result
}

无限循环

for {

fmt.Println("abc")

}

控制语句中的关键字 : goto   break   continue

函数:

返回值类型写在最后面

可返回多个值

函数可作为参数

没有默认参数,可选参数


func apply(op func(int, int) int, a, b int) int{
	fmt.Printf("Calling %s with %d, %d\n",
		runtime.FuncForPC(reflect.ValueOf(op).Pointer()).Name(), a, b)
	return op(a, b)
}


func sumArgs(values ...int) int{
    sum := 0
    for i := range values{
        sum += values[i]
    }
    return sum
}

指针

注意: 指针不能运算

var a int = 2

var pa *int =  &a

*pa = 3

fmt.Println(a)

参数传递?

Go 语言使用值传递? 引用传递?

Go 语言只有值传递一种方式。

参数传递例子

func swap(a, b *int){
    *b, *a = *a, *b;
}

数组

var array1 [5]int

array2 := [3]int{1, 2, 3}

array3 := […]int{1, 2, 3}

var grid [4][5]bool

数组的遍历

numbers := [6]int{1, 2, 3, 4, 5, 6}
for i := 0; i <len(numbers) ; i++{
    fmt.Println(numbers[i])
}

数组遍历

numbers := [6]int{1,2,3,4,5,6}
maxi := -1
maxValue := -1

for i, v := range numbers {
    if v > maxValue {
        maxi, maxValue = i, v
    }
}

sum := 0
for _, v := range numbers {
    sum += v
}

//可通过_ 省略变量

// 不仅range , 任何地方都可通过_ 省略变量

数组是值类型:

[10]int  和  [20]int 是不同类型

调用func f(arr [10]int) 会拷贝 数组

在 go 语言中一般不直接使用数组。

slice

arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

s := arr[2: 6]

s[0] = 10

Slice 本身没有数据,是对底层array的一个view

arr 的值变为 [0 1 10 3 4 5 6 7]

Reslice

arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

s := arr[2:6]    // 2, 3, 4, 5

s = s[:3]        // 2, 3, 4

s = s[1:]        //3, 4

s = arr[:]       //0, 1, 2, 3, 4, 5, 6, 7

   //arr = arr[1:5]  //报语法错误

Slice的扩展

arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

s1 := arr[2:6]   // 2, 3, 4, 5

s2 := s1[3:5]    //5, 6

s1 的值为 ? s2 的值为?

s1 值为 【2, 3, 4, 5】 ,s2的值为 [5, 6]

slice 可以向后扩展,不可以向前扩展

s[i] 不可以超越len(s);  向后扩展不可以超越底层数组cap(s)

向slice 添加元素

arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

s1 := arr[2:6]   // 2, 3, 4, 5

//s2 := s1[3:5]    //5, 6

s2 := s1[0:2]    //2 3

s2 = append(s2, 10)

// s2 的值 56 10

// s2 的值  2 310

// arr 的值    0 1 2 34 5 6 10

// arr 的值为  0 1 2 3105 6 7

添加元素时,如果超越cap,系统会重新分配更大的底层数组

由于值传递的关系,必须接收append的返回值

s = append(s ,val)

Map 的操作

创建: make(map[string]int)

获取元素 m[key]

key不存在时, 获得Value类型的初始值

用value, ok := m[key] 来判断是否存在key

用delete 删除一个key

Map的遍历:

使用range 遍历key, 或者遍历key, value对

不保证遍历顺序,如需要顺序, 需要手动对key 排序

使用len 获得元素个数

Map 的key

map使用哈希表,必须可以比较相等

除了slice ,map, function 的内建类型都可以作为key

struct 类型不包含上述字段,也可以作为key

例子: 寻找最长不包含有重复字符的子串

思路:对于每一个字母x

lastOccurred[x] 不存在,或者 < start  ====> 无需操作

lastOccurred[x] >= start ====> 更新start

更新lastOccurred[x], 更新maxLength

rune 类型

相等于go 的char

使用range 遍历pos, rune对

使用utf8.RuneCountInString 获得字符数量

使用len 获得字节长度

使用[]byte 获得字节。

其他字符串操作

Fields , Split, Join

Contains , Index

ToLower, ToUpper

Trim, TrimRight, TrimLeft

面向对象

go语言仅支持封装, 不支持继承和多态

go语言没有class, 只有struct

struct:

结构的创建:

使用自定义工厂函数;

注意返回了局部变量的地址;

type Node struct{
    Value int
    Left,Right *Node
}


func CreateNode(value int) *Node{
    return &Node{Value : value}
}

为结构定义方法

有两种方式: 值接受; 指针接受

func (node Node)Print(){
    fmt.Println(node.Value, "")
}



func (node *Node) SetValue(value int){
    if node == nil{
        fmt.Println("Setting Value to nil " + "node. Ignored.")
        return
    }
    node.Value = value
}

//只有使用指针才可以改变结构内容

//nil 指针也可以调用方法。

值接收者 是go语言特有的

怎么选择 是定义 值接收者方法  还是指针接收者方法?

要改变内容必须使用指针接收者

结构过大也考虑使用指针接收者

一致性:如有指针接收者,最好都是指针接收者。


 

如何扩充系统类型或者别人的 类型?

定义别名; 使用组合

GOPATH 环境变量

默认在 ~/go(unix, linux)

官方推荐:所有项目和第三方库都放在同一个GOPATH下

也可以将每个项目放在不同的GOPATH

接口:

//定义接口

type Retriever interface {

Get(url string) string

}

//如果有一个struct 刚好有 Get() 方法,就认为它实现了Retriever 接口

func download(r Retriever) string{

return r.Get(url)

}

duck typing

“像鸭子走路,像鸭子叫(长的像鸭子),那么就是鸭子“

描述事物的外部行为而非内部结构

严格来说 go 属于结构化类型系统,类型duck typing

python 中的duck typing

def download(retriever):

     return retriever.get(“www.imooc.com”)

运行时才知道传入的retriever 有没有get

需要注释来说明接口

C++中的duck typing

template< class R>

string download(const R& retriever){

     return retriever.get(“www.imooc.com”)

}

编译时才知道传入的retriever 有没有get

需要注释来说明。

Java 中类似的代码

<R extends Retriever>

String download(R r){

        return r.get(“www.imooc.com”)

}

传入的参数必须实现Retriever 接口

不是duck typing

go 语言的duck typing

同时需要Readable, Appendable 怎么办?(apache polygene)

同时具有python, c++ 的duck typing 的灵活性

又具有java的类型检查。

接口的定义:

接口由使用者定义

接口的实现:

特点:接口的实现是隐式的(一不小心就可能实现了某些个接口),只要实现接口的方法

接口变量有什么?

var r Retriever   //只是声明了
mockRetriever := mock.Retriever{"this is a fake imooc.com"}
r = &mockRetriever
fmt.Printf("%T %v\n", r, r)   //mock.Retriever {this is a fake imooc.com}

//我想使用指针呢
r = &realnet.Retriever{
	UserAgent :"Mozilla/5.0",
	TimeOut : time.Minute,
}
fmt.Printf("%T %v\n", r, r)   //*realnet.Retriever &{Mozilla/5.0 1m0s}

接口变量里面有什么?

接口变量自带指针

接口变量同样采用值传递,几乎不需要使用接口的指针

指针接收者者实现 只能以指针方式使用; 值接收者都可以

表示任何类型: interface{}

Type Assertion:

if mockRetriever, ok := r.(*mock.Retriever); ok {
    fmt.Println(mockRetriever.Contents)
}else{
    fmt.Println("not mock.Retriever\n")    //走这个
}

Type Switch:

func inspect(r Retriever){
    switch v := r.(type){
    case *mock.Retriever :
        fmt.Println("Contents:", v.Contents)
    case *realnet.Retriever :
        fmt.Println("UserAgent:", v.UserAgent)
    }
}

函数与闭包

func adder() func(int) int{
    sum :=0
    return func(v int) int{
        sum += v
        return sum
    }
}

func main() {
    a := adder()
    for i :=0; i< 10; i++{
        var s int
        s = a(i);
        fmt.Printf("0 + 1 + ... + %d = %d\n", i, s)
    }
}

函数式编程特点:

函数是一等公民: 参数,变量,返回值都可以是函数

高阶函数

函数-》闭包

“正统”函数式编程特点?

不可变性,不能有状态,只有常量和函数

函数只能有一个参数。

type iAdder func(int)(int, iAdder)

func adder2(base int) iAdder{
    return func(v int)(int, iAdder){
        return base +v, adder2(base+v)
    }
}



func main() {
    a := adder2(0)
    for i :=0; i< 10; i++{
        var s int
        s, a = a(i)
        fmt.Printf("0 + 1 + ... + %d = %d\n", i, s)
    }

}

python 中的闭包

python 原生支持闭包

使用__closure__ 来查看闭包内容

def adder():
    sum = 0

    def f(value):
        nonlocal sum
        sum += value
        return sum

    return f

c++ 中的闭包

过去stl 或者bootst 带有类似库

c++ 11 及以后:支持闭包

auto adder(){
    auto sum = 0;

    return [=] (int value) mutable{
        sum += value; 
        return sum;
    };

}

java 中的闭包

1.8 以后:使用Function接口和Lambda表达式来创建函数对象

匿名类或Lambda表达式均支持闭包

Function<Integer, Integer> adder(){
    final Holder<Integer> sum = new Holder<>(0);

    return (Integer value) -> {
        sum.value += value;
        return sum.value;
    };

}

go语言闭包的特点:

更为自然,不需要修饰如何访问自由变量;没有Lambda表达式, 但是有匿名函数

go 闭包 应用举例

例子一:斐波那契数列数列

func fibonacci() func() int{
    a, b := 0,1

    return func() int{
        a, b = b, a+b
        return a
    }

}

func main() {
    f := fibonacci()

    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
}

例子二: 为函数实现接口

func fibonacci() func() int{
	a, b := 0,1
	return func() int{
		a, b = b, a+b
		return a
	}
}

//为函数实现接口 --start

type intGen func() int

func (g intGen) Read( p []byte)(n int, err error){
	next := g()
	if next > 10000 {
		return 0, io.EOF
	}
	s := fmt.Sprintf("%d\n", next)

	//TODO : incorrect if p is too small !
	return strings.NewReader(s).Read(p)

}

//为实现函数接口 --end

func printFileContents(reader io.Reader){
	scanner := bufio.NewScanner(reader)

	for scanner.Scan(){
		fmt.Println(scanner.Text())
	}
}

func main() {
	//f := fibonacci()
	//
	//fmt.Println(f())
	//fmt.Println(f())

	var f intGen= fibonacci()
	printFileContents(f)
}

例子三: 使用函数来遍历二叉树 todo

defer 调用

确保调用在函数结束时发生

参数在defer 语句时计算

defer 列表为后进先出

何时使用defer调用?

Open/ Close

Lock/Unlock

PrintHeader/ PrintFooter

错误处理

_, err := os.Open("abc.txt")

if err != nil{
    if pathError, ok := err.(*os.PathError); ok{
    fmt.Println(pathError.Err)
} else {
    fmt.Println("unknown error", err)

}

panic

停止当前函数执行

一直向上返回,执行每一层的defer

如果没有遇见recover,程序退出

recover

仅在defer 调用中使用

获取panic 的值

如果无法处理,可重新panic

是 产生 error 还是  使用panic

意料之中的: 使用error. 如文件打不开

意料之外的: 使用panic .如数组越界

如何实现统一的错误处理逻辑?

错误处理综合例子:

//defer + panic + recover

//Type Assertion

func tryRecover(){
    defer func(){
        r := recover()
        if err, ok := r.(error); ok{
            fmt.Println("Error occurred:", err)
        } else {
            //panic(r)
            panic(fmt.Sprintf("I don't know what to do:%v", r))
        }
    }()


    //panic(errors.New("this is an error"))
    panic(123)
    //b := 0
    //a := 5/b
    //fmt.Println(a)
}

测试  / go测试:

传统测试 和表格测试 对比

传统测试:

测试数据和测试逻辑混合在一起

出错信息不明确

一旦一个数据出错测试全部结束

表格驱动测试:

	tests := []struct{
		a,b,c int32
	}{
		{1, 2, 3},
		{0, 2, 2},
		{-1, 1, 0},
		{math.MaxInt32, 1, math.MinInt32},
	}
	
	for _, test := range tests{
		if actual := adder(test.a, test.b); actual != test.c {
			t.Errorf("get %v, expected %v", actual, test.c)
		}
	}

分离的测试数据和逻辑

明确的错误信息

可以部分失败

go 语言的语法使得我们更容易实践表格驱动测试;

Go的test 一般以 XXX_test.go为文件名

XXX的部分一般为XXX_test.go所要测试的代码文件名;但是注意:Go并没有要求XXX必须是要测试的文件名。

test文件下的每一个test case 均必须以Test开头并且符合TestXxx形式否则go test 会直接跳过测试不执行。

test case 的入参为 t *testing.T  或 b *testing.B

t.Errorf为 打印错误信息,并且当前test case 会被跳过

t.SkipNow() 为跳过当前test, 并且直接按PASS处理,继续下一个test

Go 的test 不会保证多个TestXxx是顺序执行,但是通常会按顺序执行

func TestPrint(t *testing.T){
	t.Run("a1", func(t *testing.T) {fmt.Println("a1")})
	t.Run("a2", func(t *testing.T) {fmt.Println("a2")})
	t.Run("a3", func(t *testing.T) {fmt.Println("a3")})
}

使用t.Run来执行subtests 可以做到控制test 输出以及test的顺序

func TestMain(m *testing.M){
	fmt.Println("test main first")
	m.Run()
}

使用TestMain 作为初始化test, 并且使用m.Run()来调用其他tests可以完成一些需要初始化操作的testing,比如数据库连接,文件打开,REST服务登录等

如果没有在TestMain中调用m.Run()则除了TestMain以外的其他tests都不会被执行

Test之benchmark

benchmark 函数一般以Benchmark开头

benchmark 的case一般会跑b.N次,而且每次执行都会如此

在执行过程中会根据实际case 的执行时间是否稳定 会增加b.N的次数以 达到稳态

//非稳态的函数
func aaa(n int) int {
	for n > 0{
		n--
	}
	return n
}

func BenchmarkAll(b *testing.B) {
	for n :=0; n < b.N; n++{
		aaa(n)
	}
}

性能调优

-cpuprofile( 获取性能数据)—》 go tool pprof 查看性能数据)——〉分析慢在哪里———》优化代码——〉-cpuprofile (获取性能数据)

http 测试

  1. 通过使用假的Request/ Request

  2.  通过起服务器

package main


var tests = []struct{
	h appHandler
	code int
	message string
}{
	{errPanic, 500, "Internal Server Error"},
	{ errUserError, 400, "user error"},
	{errNotFound, 404, "Not Found"},
	{errNoPermission, 403, "Forbidden"},
	{ errUnknown, 500, "Internal Server Error"},
	{noError, 200, "no error"},

}

func noError(writer http.ResponseWriter, _ *http.Request) error{
	fmt.Fprintln(writer, "no error")
	return nil
}

func errUnknown(_ http.ResponseWriter, _ *http.Request) error{
	return errors.New("unknown error")
}

func errNoPermission(_ http.ResponseWriter, _ *http.Request) error{
	return os.ErrPermission
}

func errNotFound(_ http.ResponseWriter, _ *http.Request) error{
	return os.ErrNotExist
}

type testingUserError string

func (e testingUserError) Error() string{
	return e.Message()
}

func (e testingUserError) Message() string{
	return string(e)
}

func errUserError(_ http.ResponseWriter, _ *http.Request) error{
	return testingUserError("user error")
}

func errPanic(_ http.ResponseWriter, _ *http.Request) error{
	panic(123)
}

func TestErrWrapper( t *testing.T){
	for _, tt := range tests{
		f := errWrapper(tt.h)
		response := httptest.NewRecorder()
		request := httptest.NewRequest(http.MethodGet, "http://www.imooc.com", nil)
		f(response, request)

		verifyResponse(response.Result(), tt.code, tt.message, t)
	}
}

func verifyResponse( resp *http.Response, expectedCode int , expectedMsg string , t *testing.T){
	b, _ := ioutil.ReadAll(resp.Body)

	//body := string(b)   //会多一个换行
	body := strings.Trim(string(b), "\n")
	if resp.StatusCode != expectedCode || body != expectedMsg {
		t.Errorf("expect (%d, %s); got (%d, %s)", expectedCode, expectedMsg, resp.StatusCode, body)
	}

}

//现在不仅仅 是 测试 errwrap; 要测试 服务器了
func TestErrWrapperInServer( t *testing.T){
	for _, tt := range tests{
		f := errWrapper(tt.h)
		server := httptest.NewServer(http.HandlerFunc(f))

		resp, _ :=http.Get(server.URL)
		verifyResponse(resp, tt.code, tt.message, t)
	}
}


 

文档:

用注释写文档

在测试中加入Example

使用 go doc/godoc 来查看/生成文档

goroutine

func main() {
    var a[10]int
    for i :=0; i< 1000; i++{   //虽然开了 1000 个 实际上 活跃 3四个 线程 top查看
        go func(i int) {
            for{
                fmt.Printf("hello go routine %d\n", i)  //io  操作  go调度器 会让别人有机会执行

                //a[i]++        //直接用外边 main goroutine 就会 data race

                runtime.Gosched()

                }
        }(i)

}

    //main 执行完 就会 关闭 gorutine
    time.Sleep(time.Microsecond)
    fmt.Println(a)

}

协程 Coroutine

轻量级“线程”

非抢占式多任务处理,由协程主动交出控制权

编译器/解释器/虚拟机层面的多任务

多个协程可能在一个或多个线程上运行

其他语言中的协程

c++ Boost.Coroutine

Java: 不支持

python 中的协程

使用yield关键字实现协程

python 3.5 加入了 async def 对协程原生的支持

goroutine 的定义

任何函数只需要加上go 就能送给调度器运行

不需要在定义时区分是否是异步函数

调度器在合适的点进行切换

使用-race 来检测数据访问冲突

goroutine 可能切换点

I/O ,select  ;  函数调用(有时); channel;  runtime.Gosched() ; 等待锁

只是参考,不能保证切换,不能保证在其他地方不切换

channel

channel 

buffered channel

range

理论基础: Communication Sequential Process(CSP)

   不要通过共享内存来通信; 通过通信来共享内存

例子: 使用Channel 来等待goroutine结束 ; 以及 WaitGroup的使用,todo

使用Select来进行调度

Select 的使用

定时器的使用

在Select 中使用Nil Channel

传统同步机制

waitGroup

Mutex

Cond

http

使用http客户端发送请求

使用http.Client控制请求头部

使用httputil 简化工作

http服务器性能的分析

import _ “net/http/pprof”

访问/debug/pprof/

使用go tool pprof 分析性能

其他标准库

godoc -http :8888

https://studygolang.com/pkgdoc

go 命令

>go build  

编译go文件;

跨平台编译 env GOOS=linux GOARCH=amd64 go build

go  编译后的 可执行文件 ;双击打开(可能出现意想不到错误!) 和 ./ 打开 不一样 ,以后 还是都 ./ 方式来打开吧

>go fmt

类似 C中 lint, 统一代码风格和排版

>go run 

可以编译并运行Go源码文件;

>go get

命令主要是用来动态获取远程代码包的

> go test -v

test  // 运行当前包目录下的tests;  go  test -v  //-v打印详细信息

Benchmark

> go test -bench=.

猜你喜欢

转载自blog.csdn.net/u013862108/article/details/86558883