golang语言特性:
1. 垃圾回收 a. 内存自动回收,不需要开发人员管理内存,开发人员专注业务实现 b. 只需要new分配内存,不需要释放 2. 天然并发 a. 从语言层面支持并发,非常简单 b. goroute,轻量级线程,使创建成千上万个 goroute 成为可能 c. 基于CSP(Communicating Sequential Process)模型实现 3. channel (管道) a. 管道,类似 unix/linux 中的 pipe b. 多个 goroutine 之间通过 channel 进行通信 c. 支持任何类型 // 示例: func main() { pipe := make(chan int,3) # pipe := make(chan int,3) 做了两件事:1.声明一个管道变量pipe,pipe为int,2. 为管道pipe分配了一个新的内存空间;make 是一个关键字,表示分配一个内存空间;chan 也是一个关键字,说明该参数是个管道;3表示管道的容量,指定这个管道中最多只能放3个int pipe <- 1 # 表示把 1 放到管道pipe中 pipe <- 2 # 表示把 2 放到管道pipe中 } 4. 多返回值 a. 一个函数 返回多个值 func calc(a int,b int) (int,int){ // 两个以上的返回值要放到 () 中 sum := a + b avg := (a+b)/2 return sum,avg }
第一个go程序: helloworld.go
package main // go每一个文件都属于一个包;package 关键字说明这个文件是一个包;想让这个包成为一个可执行文件,包名必须为 main // 导入输出的包 import( "fmt" ) // go 语言的代码被编译时,每行最后会自动加一个 “;” // 定义变量a 为整型,变量b为整型;规定返回值 也是 整型 func add(a int,b int) int { var sum int // 定义一个变量 sum 为整型 sum = a + b return sum } // go 代码执行的时候会从 main 函数开始执行; main 是入口函数 func main(){ var c int c = add(100,200) // 定义的变量 c 必须有被引用才能通过编译 fmt.Println("add(100,200)=", c) fmt.Println("hello world") }
test_goroutine 测试程序:
helloworld.go
package main import( "time" // 导入时间函数 ) func main() { for i := 0; i < 100; i++ { // sum := a +b // sum := 这种定义变量的方式会根据 = 后面的变量 自动把 sum 定义成 int 整型 // 调用 test_goroutine 函数 go test_goroutine(i) // test_goroutine 函数和 main 函数 都在 main 这同一个包里面,此时包名可以省略 // go test_goroutine(i) 是并发运行的 } // 主函数运行在一个 goroute 里面,然后又起了100个test_goroutine;主线程这个 test_goroutine 要等待 那100个子线程执行完 time.Sleep(time.Second) // sleep 1 秒 }
test.go
package main // test.go 文件也是在 main 这个包里面 import ( "fmt" ) func test_goroutine(a int) { fmt.Println(a) }
终端显示如图:
管道 channel 示例1:
main.go
package main
func main() {
test_pipe()
}
pipe.go
package main import ( "fmt" ) func test_pipe(){ pipe := make(chan int, 3) pipe <- 1 pipe <- 2 pipe <- 3 fmt.Println(len(pipe)) // len(pipe) ---> 管道的长度; 管道满了后,再往管道中放 值,管道会 pend住,直到管道中有值被取出,pend住的值才会放入到管道中 var t1 int t1 =<- pipe // 从管道中取出一个值 fmt.Println(t1) }
管道 channel 示例2: 通过传参的方式,利用 管道进行 goroutine 之间的通讯
package main import ( "fmt" "time" ) func add(a int,b int,c chan int) { // c chan int ---> 形参 c 是一个int 类型的管道 var sum int sum = a + b time.Sleep(3*time.Second) // sleep 3 秒 c <- sum // 把 sum 放到管道 c 中 } func main(){ var pipe chan int // 声明一个变量 pipe 为int 的管道 pipe = make(chan int,1) // 为管道pipe分配一个新的内存空间; pipe 的容量大小为1 go add(2,5,pipe) // 开启一个新的 goroutine;把 pipe当作参数传入 add函数中 sum :=<- pipe // 从管道pipe 中取值,并赋值给类型为int的变量sum;通过这种方式主线程就能获取到 add 这个子线程中的值; 虽然 add 中 会 sleep 3 秒,但这行代码下面不需要再 sleep,因为当 add 没执行完的时候, 管道 pipe中没有值,此时程序会 pend在这行代码处 fmt.Println("sum=",sum) // time.Sleep(10*time.Second) // 此处不需要再 sleep 10 秒来等待子线程执行完毕 }
函数多返回值示例:
package main import ( "fmt" ) func calc(a int,b int) (int, int){ sum := a + b avg := (a+b)/2 return sum, avg } func main(){ sum, avg := calc(100,200) // 取出返回值 // sum, _ := calc(100, 320) // 只取一个返回值的写法 fmt.Println("sum=",sum,"avg=",avg) }
包的概念:
1. 和 python 一样,把相同功能的代码放到一个目录,称之为包;包 和 文件夹 是对应的 2. 包可以被其他包引用 3. main 包是用来生成可执行文件,第个程序只有一个 main 包 4. 包的主机用途是提高代码的可利用性 // go 语言中 所有的代码文件的都是以包的形式存在的,没有哪个代码文件不属于任何一个包
测试项目目录结构如下:
[root@NEO ~]# echo $GOPATH /root/go/project // $GOPATH 的目录 [root@NEO project]# tree /root/go/project/ /root/go/project/ ├── bin ├── example01 ├── pkg ├── src │ └── go_dev │ └── day01 │ ├── example01 │ │ └── hello.go │ └── package_example │ ├── calc │ │ ├── add.go │ │ └── sub.go │ └── main │ └── main.go └── vender 10 directories, 5 files [root@NEO project]#
example01 包的代码如下:
package main import ( "fmt" ) func main(){ fmt.Println("hello world") }
编译 go_dev/day01/example01/ 包的命令:
[root@NEO project]# go build go_dev/day01/example01 [root@NEO project]# ll total 1968 drwxr-xr-x 2 root root 4096 Jul 9 17:44 bin -rwxr-xr-x 1 root root 1997487 Jul 10 01:47 example01 // 编译后生成的可执行文件 drwxr-xr-x 2 root root 4096 Jul 9 17:44 pkg drwxr-xr-x 3 root root 4096 Jul 9 17:44 src drwxr-xr-x 2 root root 4096 Jul 9 17:44 vender [root@NEO project]# ./example01 hello world [root@NEO project]#
包测试项目 package_example示例:
package_example/main/main.go 的代码如下:
package main import ( // 从 src 目录下开始导入包 "go_dev/day01/package_example/calc" "fmt" ) // go build 的时候不用写 src 目录,因为 go 在编译的时候默认会从 src 目录下开始找 func main(){ sum := calc.Add(100,300) sub := calc.Sub(100,300) fmt.Println("sum=",sum) fmt.Println("sub=",sub) } // go build 时,先在命令行切到 /project 目录; 同时从根目录到 /project 的路径要添加到 环境变量 GOPATH 中 // go build -o bin/main.exe go_dev/day01/package_example/main // -o 参数表示编译包生成的位置; go_dev/day01/package_example/main 表示编译哪个包 (路径最前面不要加 /) // 执行上面的命令后会在 bin 目录下生成 main.exe 可执行文件 // windowns 环境下的代码
package_example/calc/add.go 代码如下:
package calc // 可导出的函数,首字母必须大写 func Add(a int,b int) int { return a +b } // windows环境下的代码
package_example/calc/sub.go 代码如下:
package calc // 可导出的函数,首字母必须大写 func Add(a int,b int) int { return a +b } // windows 环境下的代码
在linux环境下编译该项目:
[root@NEO project]# go build -o bin/main go_dev/day01/package_example/main // 编译的命令 [root@NEO project]# ll bin/ total 1952 -rwxr-xr-x 1 root root 1997628 Jul 10 02:05 main [root@NEO project]# ./bin/main sum= 400 sub= -200 [root@NEO project]#