0429 go教程 菜鸟

简介: 感觉菜鸟教程就是简单的将go语言的各知识点做一些简单的介绍,罗列,并没有串联起来,具体细节也不是很全,当然对于我这种菜鸟来说的话,感觉也还是挺不错的,在这里也给菜鸟的维护人员(据我所知好像就创始人一个人?)点赞以示感谢

菜鸟教程 -- go教程 --读后梳理总结


历时近两个月,初次正儿八经的学习一门语言,把菜鸟教程上的go语言教程都精读了一遍,虽然很基础,但是自己看下来也并不轻松,写下这篇文章的目的是为了:


  1. 复习、总结、回顾
  2. 熟悉md格式的写法
  3. 梳理出疑问点,带着疑问去精读下一本教程
  4. 分享,如果真有人看,能一起探讨下就更好了
    以下具体内容,主要分三部分来写
    一、梳理、总结菜鸟教程的具体知识点
    二、疑问点的具体总结
    三、个人未来短期学习路线的明确,以及上季度打卡的总结


一、菜鸟教程


感觉菜鸟教程就是简单的将go语言的各知识点做一些简单的介绍,罗列,并没有串联起来,具体细节也不是很全,当然对于我这种菜鸟来说的话,感觉也还是挺不错的,在这里也给菜鸟的维护人员(据我所知好像就创始人一个人?)点赞以示感谢


1.环境安装、开发工具


  1. 环境安装


直接上官网上下载就好了,然后按照教程走就好了


  1. 开发工具


个人经历了用sublime写了(主要是教程中的简单的例题)之后,然后直接在终端跑;然后再来改这个文件,再保存再改;


以及用vscode来跑。


  1. 小结


感觉这个过程,对于像我这样的一张白纸来说,如果全程自己来整的话真的会有点小折磨,很浪费时间,也会很打击斗志,如果可以的话,在不让人厌烦的情况下,多问问过来人或是让帮忙装一下会好很多。


2.结构


9例:


package main //1.声明包,package main表示一个可独立执行的程序
import "fmt" //2.定义包,告诉 Go 编译器这个程序需要使用 fmt 包(实现输入、输出的一个包)
func main(){ //3.执行main函数
  fmt.Println("Hello, World!") //4.语句表达式
}

大致的结构如上,需要注意的是:


1. fmt.Println中的P,当大写的时候才会对外包(fmt)是可见的,这被称为**导出** ,如果是小写,则对包外是不可见的(执行起来会报错),但是他们在整个包的内部是可见并且可用的.
2. "{"大括号时,不能单独起一行
3. 注释可以用// 或是/**/


3.基础语法


  1. 标记
    Go 程序可以由多个标记组成,可以是关键字,标识符,常量,字符串,符号。
  2. 行分隔符
    直接写多行就好了
    例:
fmt.Println(xxx)//Prinln就表示的换行
fmt.Println(xxxx)
  1. 注释
    常用的就是//和/**/
  2. 标识符
    用来命名变量、程序实体;第一个字符只能是字母或下划线不能是数字且不能使关键字和运算符开头
  3. 字符串连接
    用‘+’来实现
  4. 关键字
  5. 空格
    var age int
    以及:在变量和运算符间加空格,看起来更舒服
  6. 格式化字符串
    使用fmt.Printf 将需要输出的内容以指定格式来输出


4.数据类型


  1. 布尔型
    true 、 false
  2. 数字类型

  • int 整数(表有符号)
  • unit整型(无符号)

  • float32 32位浮点型数
  • float64    64位浮点型数
  • Complex64    32 位实数和虚数
  • Complex128    64位实数和虚数
  1. 字符串
    string使用 UTF-8 编码标识 Unicode 文本
  2. 派生类别
    常用的到的

  • 指针类型ptr
  • 数字类型
  • 结构类型struct
  • 通道类型 chan、ch
  • 函数类型
  • 切片类型(类数组)
  • 接口类型(interface)
  • map类型(集合)
  1. 其他
    + byte 类似uint8
    + rune类似int32


5.变量


  1. 全局变量
    通常写在func main之前(函数体之前),通常用var的形式来写
    例:
 ```
 package main
 import "fmt"
 var x, y int = 1, 2
 func main(){
  fmt.Println(x,y)
 }```
  1. 局部变量
    通常写在函数里面,用不带声明格式的方式来写
    例:
package main 
import"fmt"
func main(){
   x, y := 1, 2
   fmt.Println(x, y)
}```

6.常量


关键字为const


  • 显式类型定义 const b string = "nihao"
  • 隐式类型定义 const a = "nihao"


7.运算符


  1. 算数运算符
    +、-、*、\、++ 自增
  2. 关系运算符
    主要就是 == ,判断是否相等的时候,为== 而不是=
  3. 逻辑运算符
  • && and
  • || or
  • !not
  1. 位运算符
    需要用到的时候再实际去搜下看把
  2. 赋值运算符
    主要就是 +=
    例: C += A 等于 C = C + A
  3. 涉及到指针的运算符
    个人初理解是:
  • &值 为返回地址
  • *地址 为返回值
  1. 算数符优先级


8.条件语句


  1. if语句
  2. if...else语句
  3. if嵌套语句
  4. switch语句
  5. select语句(用于channel的相关操作)


9.循环语句


  1. for循环
  • for{} 无限循环
  • for 条件语句{}
  • for i := 0; i < x; i++ {} //最常用的
  • for key, value := range 表达式/变量{}
  1. range
    个人理解就是遍历时常用到


10.函数


  1. 函数定义
    func function_name( [parameter list] ) [return_types] {
    函数体
    }
    需要注意的是:
  • 参数列表需要用()括起来,而返回值类型不用
  • 可以没有返回值(有些功能不需要返回值)
  • 函数体:函数定义的代码集合
  1. 返回多个值


package main
import "fmt"
func swap(x int, y string) (string, int) {
   return y, x
}
func main() {
   a, b := swap(123, "Google")
   fmt.Println(a, b)
}
  1. 方法


  func (variable_name variable_data_type) function_name() [return_type]{
  /* 函数体*/
}


参照原本的例题,自己改写了一下后:


package main
import (
   "fmt"  
)
/* 定义结构体 */
type Circle struct {
  radius float64
  id float64
}
//该 method 属于 Circle 类型对象中的方法
func (c Circle) getArea() float64 {
  //c.radius 即为 Circle 类型对象中的属性
  return 3.14 * c.radius * c.id
}
func main() {
  var c1 Circle
  c1.radius = 10.00
  c1.id = 2.00
  fmt.Println("圆的面积 = ", c1.getArea())
}
  • 以上代码,只是单纯为了测试当结构体有两个值时也是行得通的。
  • 那么个人感觉方法的定义就像是:
    先定义了结构体之后,再通过像是写函数的方式一样,往这个结构体加功能即方法。调用的时候,只用调用这个结构体内的这个方法就好了。


11.变量作用域


  • 函数内定义的变量称为局部变量(函数开始之前定义的?)
package main
import "fmt"
func main() {
   /* 声明局部变量 */
   var a, b, c int
   /* 初始化参数 */
   a = 10
   b = 20
   c = a + b
   fmt.Printf ("结果: a = %d, b = %d and c = %d\n", a, b, c)
}


  • 函数外定义的变量称为全局变量
package main
import "fmt"
/* 声明全局变量 */
var g int = 20
func main() {
   /* 声明局部变量 */
   var g int = 10
   fmt.Printf ("结果: g = %d\n",  g)
}


  • 函数定义中的变量称为形式参数(形式参数会作为函数的局部变量来使用)
    -- 以下这个例子还挺方便理解的
package main
import "fmt"
/* 声明全局变量 */
var a int = 20;
func main() {
   /* main 函数中声明局部变量 */
   var a int = 10
   var b int = 20
   var c int = 0
   fmt.Printf("main()函数中 a = %d\n",  a);
   c = sum( a, b);
   fmt.Printf("main()函数中 c = %d\n",  c);
}
/* 函数定义-两数相加 */
func sum(a, b int) int {
   fmt.Printf("sum() 函数中 a = %d\n",  a);
   fmt.Printf("sum() 函数中 b = %d\n",  b);
return a + b;
}


12.数组


  • 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。
  • 通过索引(即key或是叫位置)来读取或是修改,从0开始,第二个索引为1
  • 感觉切片和数组好像,并且用的时候也感觉很相似,切片就直接放到下面了


  1. 声明数组
    var variable_name [SIZE] variable_type//var 数组名[数组大小] 数据类型
    例:
    var balance [10] float32
  2. 初始化数组
  • var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
    或是
    balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
  • 还可以通过指定下标来初始化元素:
    balance := [5]float32{1:2.0,3:7.0}
  • 如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:
  1. 访问数组元素


package main 
import "fmt"
func main(){
  var shuzu[5] int
  for i := 0; i < 5; i++ {
    shuzu[i] = 100 + i
    fmt.Printf("shuzi[%d]:=%d\n", i, shuzu[i])
  }
 } //仿照原本的例子,自己稍微改写的


13.切片slice



  1. 基本写法


package main
import (
  "fmt"
)
func main(){
  intArr := [5]int{1,22,33,66,99}
  slice :=intArr[1:3]//定义一个切片,slice就是切片名称
          //arr[1,3]表示slice引用intArr这个数组,引用arr数组起始下标为1至下标为3(不包含3)
  fmt.Println("intArr=:" ,intArr)//intArr=: [1 22 33 66 99]
  fmt.Println("slice 的内容" , slice)//slice 的内容 [22 33]
  fmt.Println("slice 长度为" , len(slice))//slice 长度为 2
  fmt.Println("slice 的容量为" ,cap(slice))// slice 的容量为 4
}


  1. 底层含义


  • slice是一个引用类型
  • slice从底层来说是一个数据结构(struct结构体)


  type slice struct{
    ptr *[2]int //这里还是不怎么理解?
    len int
    cap int
  } //所以这么看就好理解len()和cap()是这里面的内置函数了?


  1. make()
    通过make来创建切片
    基本语法:var 切片名 [] type = make([]type,len,[cap])
    参数说明:type:数据类型 len:大小 cap:指定切片容量,可选,如果分配cap,则cap>=len
    例:


package main
import (
  "fmt"
)
func main(){
  var slice2 []float64 =make([]float64,5,10)
  slice2[0]=10
  slice2[3]=20
  fmt.Println("slice2 的内容" , slice2)//slice2 的内容 [10 0 0 20 0]
  fmt.Println("slice2 长度为" , len(slice2))//slice2 长度为 5
  fmt.Println("slice2 的容量为" ,cap(slice2))//slice2 的容量为 10
}


  • 通过make方式可以创建切片可以指定切片的大小和容量
  • 如果没有给切片的个元素赋值,则为默认值
  • 通过make方式创建的切片对应的数组是由make底层维护的,对外不可见,只能通过slice去访问各个元素


  1. 直接定义
  • 原理类似make


package main
import (
  "fmt"
)
func main(){
  var slice3 []string =[]string{"tom","jack","mary"}
  fmt.Println("slice3 的内容" , slice3)//slice3 的内容 [tom jack mary]
  fmt.Println("slice3 长度为" , len(slice3))//slice3 长度为 3
  fmt.Println("slice3 的容量为" ,cap(slice3))//slice3 的容量为 3
}


  1. 以上定义(声明)切片时的区别


  • 直接引用数组的时候,是事先存在的,程序员可见的
  • 用make来创建的时候,是由切片在底层维护,程序员不可见


  1. append函数
    用append内置函数,可以对切片进行动态追加


package main
import (
  "fmt"
)
func main(){
  var slice3 []string =[]string{"tom","jack","mary"}
  fmt.Println("slice3 的内容" , slice3)//slice3 的内容 [tom jack mary]
  fmt.Println("slice3 长度为" , len(slice3))//slice3 长度为 3
  fmt.Println("slice3 的容量为" ,cap(slice3))//slice3 的容量为 3
}


  1. string和slice


  • string的底层就是一个byte数组,所以string也可以进行切片处理
str := "wang"
slice:= str[0:2]
fmt.Println(slice)//wa


  • string是不可变的,不能通过str[0]="a"来修改字符串
  • 如果需要修改,可以先将stirng转为[]byte 或[]rune修改完转换为string


str := "wang"
slice:= str[0:2]
fmt.Println(slice)//wa
arr1 := []byte(str)
arr1[0] = 'a'
str=string(arr1)
fmt.Println(str)//aang


如果包含汉字转化为rune


str := "wang"
slice:= str[0:2]
fmt.Println(slice)//wa
arr1 := []rune(str)
arr1[0] = '王'
str=string(arr1)
fmt.Println(str)//王ang


  1. 二维数组


  • 例1:


var arr [2][3]int
  arr[0][2]=1
  arr[1][1]=2
  for i:=0;i<2;i++{
    for j:=0;j<3;j++{
      fmt.Print(arr[i][j]," ")
    }
    fmt.Println()
  }
  //0 0 1
  //0 2 0


例2:


package main
import "fmt"
func main() {
   /* 数组 - 5 行 2 列*/
   var a = [5][2]int{ {0,0}, {1,2}, {2,4}, {3,6},{4,8}}
   var i, j int
   /* 输出数组元素 */
   for  i = 0; i < 5; i++ {
      for j = 0; j < 2; j++ {
         fmt.Printf("a[%d][%d] = %d\n", i,j, a[i][j] )
      }
   }
}


14.指针


  1. 指针定义
  • 一个指针变量指向一个值的地址
  • var ip *int //此时ip就已经是一个地址了
  1. *和&的用法
  • *一般用来返回地址的值
  • &一般用来返回值的地址
  1. 需要注意的是


  • 定义指针变量的时候的和具体函数体内用到的并不是同一个意思
    例:


package main
import "fmt"
func swap(x *int, y *int) { //1.这里的x和y其实就是地址来的
  var temp int
  temp = *x    /* 保存 x 地址的值 */   //所以这里要用*x来传递
  *x = *y      /* 将 y 赋值给 x */  //因为temp是int,值来的
  *y = temp    /* 将 temp 赋值给 y */
}
func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int= 200
   fmt.Printf("交换前 a 的值 : %d\n", a )
   fmt.Printf("交换前 b 的值 : %d\n", b )
   /* 调用函数用于交换值
   * &a 指向 a 变量的地址
   * &b 指向 b 变量的地址
   */
   swap(&a, &b);  //2.所以这里需要&a和&b
   fmt.Printf("交换后 a 的值 : %d\n", a )
   fmt.Printf("交换后 b 的值 : %d\n", b )
}

15.结构体


  1. 定义:
  • 结构体就是可以由各种不同数据类型的数据组成的数据类型
  • 定义结构体需要用到type和struct语句
    例:


package main
import "fmt"
type Books struct {
   title string
   author string
   subject string
   book_id int
}
func main() {
    // 创建一个新的结构体
    fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})
    // 也可以使用 key => value 格式
    fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})
    // 忽略的字段为 0 或 空
   fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})
}


  1. 赋值及访问(打印)
    都是使用.符号来操作
    例:


Book1.title = "Go 语言"
以及
fmt.Printf( "Book 2 title : %s\n", Book2.title)
  1. 结构体参数
    结构体也可以当作参数一样,直接传递给函数
  2. 结构体指针
    例子没看太懂


16.范围range


  • 用来遍历数组(array)、切片(slice)、通道(channel)、集合(map)中的index和value或是key和value
  • 通常搭配for一起用:for key, value := range xx{}
    例:


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)
    }
}

17.集合(map)


  • Map是无序的键值对,通过 key 来快速检索数据,key 类似于索引,指向数据的值。
  • Map是一种集合,无法决定它返回的顺序,因为其是用hash表来实现的。


  1. 定义map
  • 可以用map关键字
    例: var map_name map[key_type]value_type
  • 也可以用make函数(有点类似切片了)
    例:map_name := make(map[key_type]value_type)
    实例:


package main
import "fmt"
func main() {
    var countryCapitalMap map[string]string /*创建集合 */
    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 的首都不存在")
    }
}


  1. delete函数


  • 用于删除集合的元素, 参数为 map 和其对应的 key。
    例:delete(countryCapitalMap, "France")


18.接口(interface)


  • 接口也是一种数据类型
  • 它把所有具有共性的方法定义在了一起
  • 任何其他类型只要实现了这个方法就是实现了这个接口
    例:


package main
import (
    "fmt"
)
type Phone interface {
    call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
    fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
    fmt.Println("I am iPhone, I can call you!")
}
func main() {
    var phone Phone
    phone = new(NokiaPhone)
    phone.call()
    phone = new(IPhone)
    phone.call()
}
  • 在上面的例子中,我们定义了一个接口Phone,接口里面有一个方法call()。然后我们在main函数里面定义了一个Phone类型变量,并分别为之赋值为NokiaPhone和IPhone。然后调用call()方法


19.类型转换


  1. 格式
  • type_name(expression)
  • type_name 为类型,expression 为表达式。


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)
}

20.递归函数


  1. 定义


  • 就是在运行过程中调用自己
  • 语法格式如下


func recursion() {
   recursion() /* 函数调用自身 */
}
func main() {
   recursion()
}


  • 使用递归时,需要设置退出条件,否则会陷入无限循环


2.阶乘的例子


package main
import "fmt"
//其实这里的返回值的时候也可以只写uint64,但是这样的话函数体内就要再声明一下result的数据类型了,因为会用到
func Factorial(n uint64)(result uint64) {
    if (n > 0) {
        result = n * Factorial(n-1)
        return result
    }
    return 1
}
func main() {  
    var i int = 15
    fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))
}
  1. 斐波那契数列


package main
import "fmt"
func fibonacci(n int) int {
  if n < 2 {
   return n
  }
  return fibonacci(n-2) + fibonacci(n-1)
}//为什么要写成这样,直接就(n-2) + (n-1)不行吗
// -- 因为不调用自己就没办法循环,也就没办法实现效果
func main() {
    var i int
    for i = 0; i < 10; i++ {
       fmt.Printf("%d\t", fibonacci(i))
    }
} //感觉用python的写法来实现这个数列的时候会更简洁一些?


21.错误处理


看了好多遍,越看越乱,看不懂,感觉很多点没说道,就直接跳跃性的出现了,下一本书再重新捋一下


22.并发与通道


  1. 并发
  • 通过 go 关键字来开启 goroutine
  • go 函数名( 参数列表 )


package main
import (
        "fmt"
        "time"
)
func say(s string) {
        for i := 0; i < 5; i++ {
                time.Sleep(100 * time.Millisecond)
                fmt.Println(s)
        }
}
func main() {
        go say("world")
        say("hello")
}
  1. 通道(channel)
  • 用来传递数据的数据结构
  • ch <- v    // 把 v 发送到通道 ch
  • v := <-ch  // 从 ch 接收数据// 并把值赋给 v
  • 创建(声明)通道: ch := make(chan int)
    例:


package main
import "fmt"
func sum(s []int, c chan int) {
        sum := 0
        for _, v := range s {
                sum += v
        }
        c <- sum // 把 sum 发送到通道 c
}
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)
}


  1. 通道缓冲区
  • make 的第二个参数指定缓冲区大小:
  • ch := make(chan int, 100) //这里的大小是指可以存放的数量
    例:


package main 
import "fmt"
func main(){
  //这里我们定义一个可以存储整数类型的缓冲通道
  //缓冲区大小为2
  ch := make(chan int, 2)
  //因为ch是带缓冲通道的,我们可以同时发送两个数据
  //而不用立刻需要去同步读取数据
  ch <- 1000
  ch <- 2000
  //获取这两个数据
  fmt.Println(<-ch)
  fmt.Println(<-ch)
}


  1. 遍历通道与关闭通道


package main
import (
        "fmt"
)
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)
}
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)
        }
}

二、看下一本书需要更加留意的点


  1. 指针:* & 的用法,在用的时候还是会经常搞混
  2. 函数:概念还是不是很熟悉,包括参数是切片、指针的时候,就更容易搞不清了,需要多做点题?
  3. 循环
  4. 条件语句
  5. 通道
  6. 方法
  7. 切片:已经有长度这个概念了,为什么要引入容量这个概念,默认是怎样的,怎么扩充,为什么需要扩充
  8. 集合
  9. 接口
  10. 错误处理


细一想,感觉好多概念,有点印象,又不是很清楚的样子,这么看的话,大部分知识点,感觉掌握的并不是很好
目录
相关文章
|
4月前
|
XML JSON Go
Swoole与Go系列教程之WebSocket服务的应用
在 WebSocket 协议出现之前,Web 应用为了能过获取到实时的数据都是通过不断轮询服务端的接口。轮询的效率、延时很低,并且很耗费资源。
1055 2
Swoole与Go系列教程之WebSocket服务的应用
|
4月前
|
网络协议 Go
Swoole与Go系列教程之TCP服务的应用
TCP(传输控制协议)的出现是为了解决计算机网络中的数据可靠传输和连接管理的问题。在早期的计算机网络中,特别是在分组交换和互联网的发展初期,网络是不可靠的,存在丢包、错误和延迟等问题。
988 0
Swoole与Go系列教程之TCP服务的应用
|
4月前
|
网络协议 程序员 应用服务中间件
Swoole与Go系列教程之HTTP服务的应用
PHP 曾是Web开发领域佼佼者,随着业务壮大,异步和高并发方面不足显现。Swoole 曾经尝试填补空白,但局限性也比较的明显。Go 语言的崛起,简洁语法和并发优势吸引大厂使用,吸引了大多数程序员的转型。
1004 0
Swoole与Go系列教程之HTTP服务的应用
|
5月前
|
编译器 Go C++
必知的技术知识:go语言快速入门教程
必知的技术知识:go语言快速入门教程
|
6月前
|
Go
Go 语言教程
Go 语言教程
47 3
|
存储 Java Linux
Linux操作系统安装配置GO环境的详细教程
Linux操作系统安装配置GO环境的详细教程
474 1
|
6月前
|
Go 网络安全 开发工具
终极攻略!go get命令使用教程
终极攻略!go get命令使用教程
1972 0
|
6月前
|
Go
新手向:Go语言发送邮件简易教程
新手向:Go语言发送邮件简易教程
347 0
|
6月前
|
编译器 Go
Go 语言注释教程
注释是在执行时被忽略的文本。注释可用于解释代码,使其更易读。注释还可用于在测试替代代码时防止代码执行。Go支持单行或多行注释。
71 1
|
6月前
|
存储 中间件 Go
7天用Go从零实现Web框架Gee教程
7天用Go从零实现Web框架Gee教程
76 0