一、并发编程
1、goroutine
(1)创建goroutine
package main
import (
"fmt"
"time"
)
func newTask() {
for {
fmt.Println("this is a newTask")
time.Sleep(time.Second) // 延迟一秒
}
}
func main() {
go newTask() // 新建一个协程
for {
fmt.Println("this is a main goroutine")
time.Sleep(time.Second) // 延迟一秒
}
}
结果:
this is a main goroutine
this is a newTask
this is a newTask
this is a main goroutine
this is a main goroutine
this is a newTask
this is a newTask
.
.
.
(2)主goroutine先退出
package main
import (
"fmt"
"time"
)
// 主协程退出了,其他子协程也要跟着退出
func main() {
go func() {
i := 0
for {
i++
fmt.Println("子协程 i = ", i)
time.Sleep(time.Second)
}
}()
i := 0
for {
i++
fmt.Println("main i = ", i)
time.Sleep(time.Second)
if i == 2 {
break
}
}
}
结果:
main i = 1
子协程 i = 1
子协程 i = 2
main i = 2
子协程 i = 3
(3)runtime之Gosched的使用
让其他协程先执行
package main
import (
"fmt"
"runtime"
)
func main() {
go func() {
for i := 0; i < 5; i++ {
fmt.Println("hello go")
}
}()
for i := 0; i < 2; i++ {
// 让别的先执行
runtime.Gosched()
fmt.Println("hello wielun")
}
}
结果:
hello go
hello go
hello go
hello go
hello go
hello wielun
hello wielun
(4)runtime之Goexit的使用
package main
import (
"fmt"
"runtime"
)
func test() {
defer fmt.Println("3333")
// return // 终止函数
runtime.Goexit() // 终止所在的协程
fmt.Println("4444")
}
func main() {
go func() {
fmt.Println("1111")
test()
fmt.Println("2222")
}()
for {
}
}
结果:
1111
3333
(5)runtime之GOMAXPROCS的使用
设置并行计算的CPU核数的最大值,并返回之前的值
package main
import (
"fmt"
"runtime"
)
func main() {
n := runtime.GOMAXPROCS(2) // 指定以2核运算
fmt.Println("n = ", n)
for {
go fmt.Print(1)
fmt.Print(0)
}
}
结果:
00000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111000000000000000000000000111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111. . .
(6)多任务资源竞争问题
package main
import (
"fmt"
"time"
)
func Printer(str string) {
for _, data := range str {
fmt.Printf("%c", data)
time.Sleep(time.Second)
}
fmt.Printf("\n")
}
func person1() {
Printer("hello")
}
func person2() {
Printer("person")
}
func main() {
go person1()
go person2()
for {
}
}
结果:
hpeelrsloon...
2、channel
(1)通过channel实现同步
package main
import (
"fmt"
"time"
)
// 创建一个channel
var ch = make(chan int)
func Printer(str string) {
for _, data := range str {
fmt.Printf("%c", data)
time.Sleep(time.Second)
}
fmt.Printf("\n")
}
func person1() {
Printer("hello")
ch <- 666
}
func person2() {
<-ch
Printer("person")
}
func main() {
go person1()
go person2()
for {
}
}
结果:
hello
person
(2)通过channel实现同步和数据交互
package main
import (
"fmt"
"time"
)
func main() {
// 创建channel
ch := make(chan string)
defer fmt.Println("主协程结束")
go func() {
defer fmt.Println("子协程调用完毕")
for i := 0; i < 2; i++ {
fmt.Println("子协程i = ", i)
time.Sleep(time.Second)
}
ch <- "我是子协程,工作完毕"
}()
str := <-ch //没有数据阻塞
fmt.Println("str = ", str)
}
结果:
子协程i = 0
子协程i = 1
子协程调用完毕
str = 我是子协程,工作完毕
主协程结束
(3)无缓冲的channel
创建格式:make(chan Type) //等价于make(chan Type, 0)
package main
import (
"fmt"
"time"
)
func main() {
// 创建一个无缓冲的channel
ch := make(chan int, 0)
// 缓冲区剩余数据个数
fmt.Printf("len(ch) = %d, cap(ch) = %d\n", len(ch), cap(ch))
go func() {
for i := 0; i < 3; i++ {
fmt.Printf("子协程i = %d ", i)
ch <- i //往chan写内容
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 3; i++ {
num := <-ch // 读管道中内容。没有内容前,阻塞
fmt.Println("num = ", num)
}
}
结果:
len(ch) = 0, cap(ch) = 0
子协程i = 0 子协程i = 1 num = 0
num = 1
子协程i = 2 num = 2
(3)有缓冲的channel
创建格式:make(chan Type, capacity)
package main
import (
"fmt"
"time"
)
func main() {
// 创建一个有缓冲的channel
ch := make(chan int, 3)
// 缓冲区剩余数据个数
fmt.Printf("len(ch) = %d, cap(ch) = %d\n", len(ch), cap(ch))
go func() {
for i := 0; i < 10; i++ {
ch <- i //往chan写内容
fmt.Printf("子协程[%d]: len(ch) = %d, cap(ch) = %d\n", i, len(ch), cap(ch))
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 10; i++ {
num := <-ch // 读管道中内容。没有内容前,阻塞
fmt.Println("num = ", num)
}
}
结果:
len(ch) = 0, cap(ch) = 3
子协程[0]: len(ch) = 1, cap(ch) = 3
子协程[1]: len(ch) = 2, cap(ch) = 3
子协程[2]: len(ch) = 3, cap(ch) = 3
num = 0
num = 1
num = 2
num = 3
子协程[3]: len(ch) = 3, cap(ch) = 3
子协程[4]: len(ch) = 0, cap(ch) = 3
子协程[5]: len(ch) = 1, cap(ch) = 3
子协程[6]: len(ch) = 2, cap(ch) = 3
子协程[7]: len(ch) = 3, cap(ch) = 3
num = 4
num = 5
num = 6
num = 7
num = 8
子协程[8]: len(ch) = 3, cap(ch) = 3
子协程[9]: len(ch) = 0, cap(ch) = 3
num = 9
(4)关闭channel
关闭channel后,无法向channel再发送数据(引发panic错误后导致接收立即返回零值)
关闭cahnnel后,可以继续向channel接收数据
对于nil channel,无论收发都会被阻塞
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
// 不需要再写数据时,关闭channel
close(ch)
}()
// 1、通过for实现
// for {
// // 如果ok为true, 说明管道没有关闭
// if num, ok := <-ch; ok == true {
// fmt.Println("num = ", num)
// } else {
// break
// }
// }
// 2、通过range实现
for num := range ch {
fmt.Println("num = ", num)
}
}
结果:
num = 0
num = 1
num = 2
num = 3
num = 4
(5)单向channel特性
声明:
chan<-: 表示数据进入管道,要把数据写进管道,对于调用者就是输出
<-chan: 表示数据从管道出来,对于调用者就是得到管道的数据,当然就是输入
var ch1 chan int // ch1是一个正常的channel,不是单向的
var ch chan<- float64 // ch2是单向channel,只用于写float64数据
var ch3 <-chan int // ch3是单向channel,只用于读取int数据
package main
func main() {
// 创建一个channel,双向的
ch := make(chan int)
// 双向channel能隐式转换为单向channel
var writeCh chan<- int = ch //只能写,不能读
var readCh <-chan int = ch //只能读,不能写
writeCh <- 666 //写
<-readCh //读
// 单向无法转换为双向
// var ch2 chan int = writeCh
}
(6)单向channel的应用
package main
import (
"fmt"
)
// 此通道只能写,不能读
func producer(out chan<- int) {
for i := 0; i < 10; i++ {
out <- i * i
}
close(out)
}
// 此通道只能读,不能写
func consumer(in <-chan int) {
for num := range in {
fmt.Println("num = ", num)
}
}
func main() {
ch := make(chan int)
// 生产者,生产数字,写入channel
go producer(ch)
// 消费者,从channel读取数字,打印
consumer(ch)
}
结果:
num = 0
num = 1
num = 4
num = 9
num = 16
num = 25
num = 36
num = 49
num = 64
num = 81
(7)定时器之Timer的使用
时间到了,只会响应一次,不会多次写入
结果:
(8)定时器之Timer实现延时功能
package main
import (
"fmt"
"time"
)
func main() {
// 延时2s
// 方式一
// timer := time.NewTimer(2 * time.Second)
// <-timer.C
// fmt.Println("时间到")
// 方式二
// time.Sleep(2*time.Second)
// fmt.Println("时间到")
// 方式三
<-time.After(2 * time.Second) // 定时2s,阻塞2s,2s后产生一个事件,往channel写内容
fmt.Println("时间到")
}
(9)定时器之停止和重置定时器
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(3 * time.Second)
timer.Reset(1 * time.Second) // 重新设置为1s
<-timer.C
fmt.Println("时间到")
}
// 停止
// func main01() {
// timer := time.NewTimer(3 * time.Second)
// go func() {
// <-timer.C
// fmt.Println("子协程可以打印了,定时器时间到")
// }()
// timer.Stop() //停止定时器
// for {
// }
// }
(10)定时器之Ticker的使用
Ticker是一个定时触发的计时器,它会以一个间隔(interval)往channel发送一个事件(当前时间),而channel的接收者可以以固定的时间间隔从channel中读取事件
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(1 * time.Second)
i := 0
for {
<-ticker.C
i++
fmt.Println("i = ", i)
if i == 5 {
ticker.Stop()
break
}
}
}
结果:
i = 1
i = 2
i = 3
i = 4
i = 5
3、select
通过select可以监听channel上的数据流动
如果多个条件同时满足,会随机选一个优先执行
(1)fibonacci数列
package main
import (
"fmt"
)
// ch写,quit读
func fibonacci(ch chan<- int, quit <-chan bool) {
x, y := 1, 1
for {
// 监听channel数据的流动
select {
case ch <- x:
x, y = y, x+y
case flag := <-quit:
fmt.Println("flag = ", flag)
return
}
}
}
func main() {
ch := make(chan int) //数字通信
quit := make(chan bool) //程序是否结束
// 消费者,从channel读取内容
go func() {
for i := 0; i < 8; i++ {
num := <-ch
fmt.Println(num)
}
// 可以停止
quit <- true
}()
// 生产者,产生数字,写入channel
fibonacci(ch, quit)
}
结果:
1
1
2
3
5
8
13
21
flag = true
(2)通过select实现超时
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
quit := make(chan bool)
// 协程
go func() {
for {
select {
case num := <-ch:
fmt.Println("num = ", num)
case <-time.After(3 * time.Second):
fmt.Println("超时")
quit <- true
}
}
}()
for i := 0; i < 5; i++ {
ch <- i
time.Sleep(time.Second)
}
<-quit
fmt.Println("程序结束")
}
结果:
num = 0
num = 1
num = 2
num = 3
num = 4
超时
程序结束
二、Socket编程
1、CS模型
(1)TCP初体验
<1> TCP服务器
package main
import (
"fmt"
"net"
)
func main() {
// 监听
listener, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("err = ", err)
return
}
defer listener.Close()
// 阻塞等待用户链接
conn, err := listener.Accept()
if err != nil {
fmt.Println("err = ", err)
return
}
// 接收用户得请求
buf := make([]byte, 1024)
n, err1 := conn.Read(buf)
if err1 != nil {
fmt.Println("err1 = ", err1)
return
}
fmt.Println("buf = ", string(buf[:n]))
defer conn.Close() //关闭当前用户链接
}
<2> TCP客户端
package main
import (
"fmt"
"net"
)
func main() {
// 主动连接服务器
conn, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("err = ", err)
return
}
defer conn.Close()
// 发送数据
conn.Write([]byte("hello wielun"))
}
(2)简单版并发服务器
<1> TCP服务器
package main
import (
"fmt"
"net"
"strings"
)
func HandleConn(conn net.Conn) {
// 自动关闭
defer conn.Close()
// 获取客户端的网络地址信息
addr := conn.RemoteAddr().String()
fmt.Println(addr, "connect sucessful")
buf := make([]byte, 2048)
for {
// 读取用户数据
n, err := conn.Read(buf)
if err != nil {
fmt.Println("err = ", err)
return
}
fmt.Printf("[%s]: %s ", addr, string(buf[:n]))
if "exit" == string(buf[:n-2]) { // 发送时多了换行字符
fmt.Println(addr, "exit")
}
// 转换为大写,在发送给用户
conn.Write([]byte(strings.ToUpper(string(buf[:n]))))
}
}
func main() {
// 监听
listener, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("err = ", err)
return
}
defer listener.Close()
// 接收多个用户
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("err = ", err)
return
}
// 处理用户请求,新建一个协程
go HandleConn(conn)
}
}
<2> Client
package main
import (
"fmt"
"net"
"os"
)
func main() {
// 主动连接服务器
conn, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("net.Dial err = ", err)
return
}
// 关闭连接
defer conn.Close()
go func() {
str := make([]byte, 1024)
// 从键盘输入内容,给服务器发送内容
for {
n, err := os.Stdin.Read(str) // 从键盘读取内容,放在str
if err != nil {
fmt.Println("os.Stdin.Read err = ", err)
return
}
// 内容发送给服务器
conn.Write(str[:n])
}
}()
// 接受服务器回复的数据
// 切片缓冲
buf := make([]byte, 1024)
for {
n, err := conn.Read(buf)
if err != nil {
fmt.Println("conn.Read err = ", err)
return
}
fmt.Println(string(buf[:n])) //打印接收到的内容
}
}
2、文件传输
(1)获取文件属性
package main
import (
"fmt"
"os"
)
func main() {
list := os.Args
if len(list) != 2 {
fmt.Println("useage: xxx file")
return
}
fileName := list[1]
info, err := os.Stat(fileName)
if err != nil {
fmt.Println("err = ", err)
return
}
fmt.Println("name = ", info.Name())
fmt.Println("size = ", info.Size())
}
结果:
PS D:\MySoftWare\Go Projects\src\wielun666@gmail.com\02> go run .\04_获取文件属性.go D:\MySoftWare\TIM\Bin\QQScLauncher.exe
name = QQScLauncher.exe
size = 58960
(2)send
package main
import (
"fmt"
"io"
"net"
"os"
)
func SendFile(path string, conn net.Conn) {
// 以只读方式打开文件
f, err := os.Open(path)
if err != nil {
fmt.Println("os.Open err = ", err)
return
}
defer f.Close()
buf := make([]byte, 1024*4)
// 读文件内容发送
for {
n, err := f.Read(buf)
if err != nil {
if err == io.EOF {
fmt.Println("文件发送完毕")
} else {
fmt.Println("f.Read err = ", err)
}
return
}
// 发送内容
conn.Write(buf[:n])
}
}
func main() {
// 提示输入文件
fmt.Println("请输入需要传输得文件:")
var path string
fmt.Scan(&path)
// 获取文件名
info, err := os.Stat(path)
if err != nil {
fmt.Println("err = ", err)
return
}
// 主动连接服务器
conn, err1 := net.Dial("tcp", "127.0.0.1:8080")
if err1 != nil {
fmt.Println("err1 = ", err1)
return
}
defer conn.Close()
// 给接收方,先发送文件名
_, err = conn.Write([]byte(info.Name()))
if err != nil {
fmt.Println("err = ", err)
return
}
// 接收对方得回复
var n int
buf := make([]byte, 1024)
n, err = conn.Read(buf)
if err != nil {
fmt.Println("conn.Read err = ", err)
return
}
if "ok" == string(buf[:n]) {
// 发送文件内容
SendFile(path, conn)
}
}
(3)recv
package main
import (
"fmt"
"io"
"net"
"os"
)
func RecvFile(fileName string, conn net.Conn) {
// 新建文件
f, err := os.Create(fileName)
if err != nil {
fmt.Println("os.Create err = ", err)
return
}
buf := make([]byte, 1024*4)
for {
n, err := conn.Read(buf)
if err != nil {
if err == io.EOF {
fmt.Println("文件接收完毕")
} else {
fmt.Println("conn.Read err = ", err)
}
return
}
f.Write(buf[:n])
}
}
func main() {
// 监听
listenner, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("net.Liste err = ", err)
return
}
defer listenner.Close()
// 阻塞等待用户连接
conn, err1 := listenner.Accept()
if err1 != nil {
fmt.Println("listenner.Accept err1 = ", err1)
return
}
defer conn.Close()
buf := make([]byte, 1024)
var n int
n, err = conn.Read(buf)
if err != nil {
fmt.Println("conn.Read err = ", err)
return
}
fileName := string(buf[:n])
// 回复ok
conn.Write([]byte("ok"))
// 接收文件内容
RecvFile(fileName, conn)
}
三、HTTP编程
1、http服务器
package main
import (
"fmt"
"net/http"
)
// w,给客户端回复数据
// r,读取客户端发送的数据
func HandConn(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method)
fmt.Println("url:", r.URL.Path)
fmt.Println("header:", r.Header)
fmt.Println("body:", r.Body)
w.Write([]byte("<h1>hello wielun</h1>"))
}
func main() {
http.HandleFunc("/go", HandConn)
// 监听绑定
http.ListenAndServe(":8080", nil)
}
结果:
method: GET
url: /go
header: map[Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3] Accept-Encoding:[gzip, deflate, br] Accept-Language:[zh-CN,zh;q=0.9] Cache-Control:[max-age=0] Connection:[keep-alive] Sec-Fetch-Mode:[navigate] Sec-Fetch-Site:[none] Sec-Fetch-User:[?1] Upgrade-Insecure-Requests:[1] User-Agent:[Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36]]
body: {}
2、http客户端
package main
import (
"fmt"
"net/http"
)
func main() {
resp, err := http.Get("http://www.baidu.com")
if err != nil {
fmt.Println("http.Get err = ", err)
return
}
defer resp.Body.Close()
fmt.Println("Status = ", resp.Status)
fmt.Println("StatusCode = ", resp.StatusCode)
fmt.Println("Header = ", resp.Header)
// fmt.Println("Body = ", resp.Body)
buf := make([]byte, 4*1024)
var tmp string
for {
n, err := resp.Body.Read(buf)
if n == 0 {
fmt.Println("read err = ", err)
break
}
tmp += string(buf[:n])
}
fmt.Println("tmp = ", tmp)
}