1)defer调用
func tryDefer() {
defer fmt.Println(1)
defer fmt.Println(2)
panic("error occurred")
}
func main() {
tryDefer() // 2 1
}
func writeFile(filename string) {
file, err := os.Create(filename)
if err != nil {
panic(err)
}
defer file.Close()
writer := bufio.NewWriter(file)
defer writer.Flush()
fmt.Fprintf(writer, "hello world")
}
func main() {
writeFile("hello world.txt")
}
func tryDefer() {
for i := 0; i < 100; i++ {
defer fmt.Println(i) // 30 29 28 ...
if i == 30 {
panic("printed too many")
}
}
}
func main() {
tryDefer()
}
2)何时使用defer调用
func writeFile(filename string) {
file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666) // 文件已存在时报错
if err != nil {
if pathError, ok := err.(*os.PathError); !ok {
panic(err)
} else {
fmt.Println(pathError.Op, pathError.Path, pathError.Err) // open hello world.txt file exists
}
return
}
defer file.Close()
writer := bufio.NewWriter(file)
defer writer.Flush()
fmt.Fprintf(writer, "hello world")
}
func main() {
writeFile("hello world.txt")
}
package filelisting
import (
"io/ioutil"
"net/http"
"os"
)
func HandleFileListing(writer http.ResponseWriter, request *http.Request) error {
path := request.URL.Path[len("/list/"):]
file, err := os.Open(path)
defer file.Close()
if err != nil {
return err
}
all, err := ioutil.ReadAll(file)
if err != nil {
return err
}
writer.Write(all)
return nil
}
package main
import (
"learngo/errhandling/filelistingserver/filelisting"
"log"
"net/http"
"os"
)
type appHandler func(writer http.ResponseWriter, request *http.Request) error
func errWrapper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
err := handler(writer, request)
if err != nil {
log.Printf("Error handling request:%s\n", err.Error())
code := http.StatusOK
switch {
case os.IsNotExist(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(writer, http.StatusText(code), code)
}
}
}
func main() {
http.HandleFunc("/list/", errWrapper(filelisting.HandleFileListing))
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
项目根目录下有hello world.txt,访问http://localhost:8888/list/hello%20world.txt,返回hello world
访问http://localhost:8888/list/,返回Not Found
拷贝一个hello world2.txt文件,修改访问权限
sudo cp hello\ world.txt hello\ world2.txt
sudo chmod 500 hello\ world2.txt
访问http://localhost:8888/list/hello%20world2.txt,返回Forbidden
1)panic
2)recover
func tryRecover() {
defer func() {
r := recover()
if err, ok := r.(error); ok {
fmt.Println("Error occurred:", err)
} else {
panic(r)
}
}()
panic(errors.New("this is an error"))
}
func main() {
tryRecover()
}
package filelisting
import (
"io/ioutil"
"net/http"
"os"
"strings"
)
const prefix = "/list/"
type userError string
func (e userError) Error() string {
return e.Message()
}
func (e userError) Message() string {
return string(e)
}
func HandleFileListing(writer http.ResponseWriter, request *http.Request) error {
if strings.Index(request.URL.Path, prefix) != 0 {
return userError("Path must start with " + prefix)
}
path := request.URL.Path[len(prefix):]
file, err := os.Open(path)
defer file.Close()
if err != nil {
return err
}
all, err := ioutil.ReadAll(file)
if err != nil {
return err
}
writer.Write(all)
return nil
}
package main
import (
"learngo/errhandling/filelistingserver/filelisting"
"log"
"net/http"
"os"
)
type appHandler func(writer http.ResponseWriter, request *http.Request) error
func errWrapper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
defer func() {
if r := recover(); r != nil {
log.Printf("Panic: %v", r)
http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
}()
err := handler(writer, request)
if err != nil {
log.Printf("Error handling request:%s\n", err.Error())
if userErr, ok := err.(userError); ok {
http.Error(writer, userErr.Message(), http.StatusBadRequest)
return
}
code := http.StatusOK
switch {
case os.IsNotExist(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(writer, http.StatusText(code), code)
}
}
}
type userError interface {
error
Message() string
}
func main() {
http.HandleFunc("/", errWrapper(filelisting.HandleFileListing))
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
访问http://localhost:8888/,返回Path must start with /list/
error vs panic:
func main() {
for i := 0; i < 10; i++ {
go func(i int) {
for {
fmt.Printf("Hello from goroutine %d\n", i)
}
}(i)
}
time.Sleep(time.Millisecond)
}
func main() {
var a [10]int
for i := 0; i < 10; i++ {
go func() {
for {
a[i]++
runtime.Gosched()
}
}()
}
time.Sleep(time.Millisecond)
}
执行报错:
panic: runtime error: index out of range [10] with length 10
检测数据访问冲突:
go run -race goroutine.go
1)协程coroutine
子程序是协程的一个特例
2)goroutine
3)goroutine的定义
4)goroutine可能的切换点
runtime.Gosched()
只是参考,不能保证切换,不能保证在其他地方不切换
func channelDemo() {
c := make(chan int)
go func() {
for {
n := <-c
fmt.Println(n)
}
}()
c <- 1
c <- 2
time.Sleep(time.Second)
}
func main() {
channelDemo()
}
channel数组:
func worker(id int, c chan int) {
for {
fmt.Printf("Worker %d received %c\n", id, <-c)
}
}
func channelDemo() {
var channels [10]chan int
for i := 0; i < 10; i++ {
channels[i] = make(chan int)
go worker(i, channels[i])
}
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Second)
}
func main() {
channelDemo()
}
channel作为返回值:
func createWorker(id int) chan<- int {
c := make(chan int)
go func() {
for {
fmt.Printf("Worker %d received %c\n", id, <-c)
}
}()
return c
}
func channelDemo() {
var channels [10]chan<- int
for i := 0; i < 10; i++ {
channels[i] = createWorker(i)
}
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Second)
}
func main() {
channelDemo()
}
channel缓冲区:
func worker(id int, c chan int) {
for {
fmt.Printf("Worker %d received %c\n", id, <-c)
}
}
func bufferedChannel() {
c := make(chan int, 3)
go worker(0, c)
c <- 'a'
c <- 'b'
c <- 'c'
time.Sleep(time.Second)
}
func main() {
bufferedChannel()
}
关闭channel:
func worker(id int, c chan int) {
for n := range c {
fmt.Printf("Worker %d received %c\n", id, n)
}
}
func channelClose() {
c := make(chan int, 3)
go worker(0, c)
c <- 'a'
c <- 'b'
c <- 'c'
close(c)
time.Sleep(time.Second)
}
func main() {
channelClose()
}
type worker struct {
in chan int
done chan bool
}
func doWorker(id int, c chan int, done chan bool) {
for n := range c {
fmt.Printf("Worker %d received %c\n", id, n)
done <- true
}
}
func createWorker(id int) worker {
w := worker{
in: make(chan int),
done: make(chan bool),
}
go doWorker(id, w.in, w.done)
return w
}
func channelDemo() {
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWorker(i)
}
for i, worker := range workers {
worker.in <- 'a' + i
}
for _, worker := range workers {
<-worker.done
}
for i, worker := range workers {
worker.in <- 'A' + i
}
for _, worker := range workers {
<-worker.done
}
}
func main() {
channelDemo()
}
使用WaitGroup实现:
type worker struct {
in chan int
done func()
}
func doWorker(id int, w worker) {
for n := range w.in {
fmt.Printf("Worker %d received %c\n", id, n)
w.done()
}
}
func createWorker(id int, wg *sync.WaitGroup) worker {
w := worker{
in: make(chan int),
done: func() {
wg.Done()
},
}
go doWorker(id, w)
return w
}
func channelDemo() {
var wg sync.WaitGroup
wg.Add(20)
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWorker(i, &wg)
}
for i, worker := range workers {
worker.in <- 'a' + i
}
for i, worker := range workers {
worker.in <- 'A' + i
}
wg.Wait()
}
func main() {
channelDemo()
}
package node
import "fmt"
type TreeNode struct {
Value int
Left, Right *TreeNode
}
func (node TreeNode) Print() {
fmt.Print(node.Value, " ")
}
func CreateNode(value int) *TreeNode {
return &TreeNode{Value: value}
}
package node
import (
"fmt"
)
func (node *TreeNode) Traverse() {
node.TraverseFunc(func(node *TreeNode) {
node.Print()
})
fmt.Println()
}
// 中序遍历
func (node *TreeNode) TraverseFunc(f func(*TreeNode)) {
if node == nil {
return
}
node.Left.TraverseFunc(f)
f(node)
node.Right.TraverseFunc(f)
}
func (node *TreeNode) TraverseWithChannel() chan *TreeNode {
out := make(chan *TreeNode)
go func() {
node.TraverseFunc(func(node *TreeNode) {
out <- node
})
close(out)
}()
return out
}
package main
import (
"fmt"
"learngo/tree/node"
)
func main() {
root := node.CreateNode(3)
root.Left = node.CreateNode(0)
root.Right = node.CreateNode(5)
root.Right.Left = node.CreateNode(4)
root.Left.Right = node.CreateNode(2)
root.Traverse() // 0 2 3 4 5
nodeCount := 0
root.TraverseFunc(func(node *node.TreeNode) {
nodeCount++
})
fmt.Println("node count:", nodeCount) // node count: 5
c := root.TraverseWithChannel()
maxNode := 0
for node := range c {
if node.Value > maxNode {
maxNode = node.Value
}
}
fmt.Println("max node value:", maxNode) // max node value: 5
}
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
out <- i
i++
}
}()
return out
}
func worker(id int, c chan int) {
for n := range c {
time.Sleep(time.Second)
fmt.Printf("Worker %d received %d\n", id, n)
}
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)
return c
}
func main() {
var c1, c2 = generator(), generator()
var worker = createWorker(0)
var values []int
tm := time.After(10 * time.Second)
tick := time.Tick(time.Second)
for {
var activeWorker chan<- int
var activeValue int
if len(values) > 0 {
activeWorker = worker
activeValue = values[0]
}
select {
case n := <-c1:
values = append(values, n)
case n := <-c2:
values = append(values, n)
case activeWorker <- activeValue:
values = values[1:]
// 200毫秒没有数据打印timeout
case <-time.After(200 * time.Millisecond):
fmt.Println("timeout")
// 每秒打印队列长度
case <-tick:
fmt.Println("queue len =", len(values))
// 程序运行10秒后结束
case <-tm:
fmt.Println("end")
return
}
}
}
实现atomicInt:
package main
import (
"fmt"
"sync"
"time"
)
type atomicInt struct {
value int
lock sync.Mutex
}
func (a *atomicInt) increment() {
a.lock.Lock()
defer a.lock.Unlock()
a.value++
}
func (a *atomicInt) get() int {
a.lock.Lock()
defer a.lock.Unlock()
return a.value
}
func main() {
var a atomicInt
for i := 0; i < 1000; i++ {
go func() {
a.increment()
}()
}
time.Sleep(5 * time.Second)
fmt.Println(a.get())
}
func msgGen(name string) <-chan string {
c := make(chan string)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
c <- fmt.Sprintf("service %s: message %d", name, i)
i++
}
}()
return c
}
func fanIn(c1, c2 <-chan string) chan string {
c := make(chan string)
go func() {
for {
c <- <-c1
}
}()
go func() {
for {
c <- <-c2
}
}()
return c
}
func main() {
m1, m2 := msgGen("service1"), msgGen("service2")
m := fanIn(m1, m2)
for {
fmt.Println(<-m)
}
}
func msgGen(name string) <-chan string {
c := make(chan string)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
c <- fmt.Sprintf("service %s: message %d", name, i)
i++
}
}()
return c
}
func fanInBySelect(c1, c2 <-chan string) chan string {
c := make(chan string)
go func() {
for {
select {
case m := <-c1:
c <- m
case m := <-c2:
c <- m
}
}
}()
return c
}
func main() {
m1, m2 := msgGen("service1"), msgGen("service2")
m := fanInBySelect(m1, m2)
for {
fmt.Println(<-m)
}
}
func msgGen(name string) <-chan string {
c := make(chan string)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
c <- fmt.Sprintf("service %s: message %d", name, i)
i++
}
}()
return c
}
func fanIn(chs ...<-chan string) chan string {
c := make(chan string)
for _, ch := range chs {
go func(in <-chan string) {
for {
c <- <-in
}
}(ch)
}
return c
}
func main() {
m1, m2 := msgGen("service1"), msgGen("service2")
m := fanIn(m1, m2)
for {
fmt.Println(<-m)
}
}
非阻塞等待:
func msgGen(name string) <-chan string {
c := make(chan string)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
c <- fmt.Sprintf("service %s: message %d", name, i)
i++
}
}()
return c
}
func nonBlockingWait(c <-chan string) (string, bool) {
select {
case m := <-c:
return m, true
default:
return "", false
}
}
func main() {
m1, m2 := msgGen("service1"), msgGen("service2")
for {
fmt.Println(<-m1)
if m, ok := nonBlockingWait(m2); ok {
fmt.Println(m)
} else {
fmt.Println("no message from service2")
}
}
}
超时机制:
func msgGen(name string) <-chan string {
c := make(chan string)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(5000)) * time.Millisecond)
c <- fmt.Sprintf("service %s: message %d", name, i)
i++
}
}()
return c
}
func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
select {
case m := <-c:
return m, true
case <-time.After(timeout):
return "", false
}
}
func main() {
m1 := msgGen("service1")
for {
if m, ok := timeoutWait(m1, 2*time.Second); ok {
fmt.Println(m)
} else {
fmt.Println("timeout")
}
}
}
任务中断/退出:
func msgGen(name string, done chan struct{}) <-chan string {
c := make(chan string)
go func() {
i := 0
for {
select {
case <-time.After(time.Duration(rand.Intn(5000)) * time.Millisecond):
c <- fmt.Sprintf("service %s: message %d", name, i)
case <-done:
fmt.Println("cleaning up")
return
}
i++
}
}()
return c
}
func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
select {
case m := <-c:
return m, true
case <-time.After(timeout):
return "", false
}
}
func main() {
done := make(chan struct{})
m1 := msgGen("service1", done)
for i := 0; i < 5; i++ {
if m, ok := timeoutWait(m1, time.Second); ok {
fmt.Println(m)
} else {
fmt.Println("timeout")
}
}
done <- struct{}{}
time.Sleep(time.Second)
}
优雅退出:
func msgGen(name string, done chan struct{}) <-chan string {
c := make(chan string)
go func() {
i := 0
for {
select {
case <-time.After(time.Duration(rand.Intn(5000)) * time.Millisecond):
c <- fmt.Sprintf("service %s: message %d", name, i)
case <-done:
fmt.Println("cleaning up")
time.Sleep(2 * time.Second)
fmt.Println("cleanup done")
done <- struct{}{}
return
}
i++
}
}()
return c
}
func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
select {
case m := <-c:
return m, true
case <-time.After(timeout):
return "", false
}
}
func main() {
done := make(chan struct{})
m1 := msgGen("service1", done)
for i := 0; i < 5; i++ {
if m, ok := timeoutWait(m1, time.Second); ok {
fmt.Println(m)
} else {
fmt.Println("timeout")
}
}
done <- struct{}{}
<-done
}
迷宫文件maze.in
6 5
0 1 0 0 0
0 0 0 1 0
0 1 0 1 0
1 1 1 0 0
0 1 0 0 1
0 1 0 0 0
代码实现:
func readMaze(filename string) [][]int {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
var row, col int
fmt.Fscanf(file, "%d %d", &row, &col)
maze := make([][]int, row)
for i := range maze {
maze[i] = make([]int, col)
for j := range maze[i] {
fmt.Fscanf(file, "%d", &maze[i][j])
}
}
return maze
}
type point struct {
i, j int
}
var dirs = [4]point{{-1, 0}, {0, -1}, {1, 0}, {0, 1}}
func (p point) add(r point) point {
return point{p.i + r.i, p.j + r.j}
}
func (p point) at(grid [][]int) (int, bool) {
if p.i < 0 || p.i >= len(grid) {
return 0, false
}
if p.j < 0 || p.j >= len(grid[p.i]) {
return 0, false
}
return grid[p.i][p.j], true
}
func walk(maze [][]int, start, end point) [][]int {
steps := make([][]int, len(maze))
for i := range steps {
steps[i] = make([]int, len(maze[i]))
}
Q := []point{start}
for len(Q) > 0 {
cur := Q[0]
Q = Q[1:]
if cur == end {
break
}
for _, dir := range dirs {
next := cur.add(dir)
// maze at next is 0
// and steps at next is 0
// and next != start
val, ok := next.at(maze)
if !ok || val == 1 {
continue
}
val, ok = next.at(steps)
if !ok || val != 0 {
continue
}
if next == start {
continue
}
curSteps, _ := cur.at(steps)
steps[next.i][next.j] = curSteps + 1
Q = append(Q, next)
}
}
return steps
}
func main() {
maze := readMaze("maze/maze.in")
steps := walk(maze, point{0, 0}, point{len(maze) - 1, len(maze[0]) - 1})
for _, row := range steps {
for _, val := range row {
fmt.Printf("%3d", val)
}
fmt.Println()
}
}
1)使用http客户端发送请求
func main() {
resp, err := http.Get("https://www.imooc.com")
if err != nil {
panic(err)
}
defer resp.Body.Close()
s, err := httputil.DumpResponse(resp, true)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", s)
}
2)使用http.Client控制请求头部等
func main() {
request, err := http.NewRequest(http.MethodGet, "https://www.imooc.com", nil)
request.Header.Add("User-Agent",
"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1")
client := http.Client{
CheckRedirect: func(
req *http.Request,
via []*http.Request) error {
fmt.Println("Redirect:", req)
return nil
},
}
resp, err := client.Do(request)
if err != nil {
panic(err)
}
defer resp.Body.Close()
s, err := httputil.DumpResponse(resp, true)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", s)
}
type Order struct {
Id string `json:"id"`
Name string `json:"name,omitempty"` // omitempty:省略空
Quantity int `json:"quantity"`
TotalPrice float64 `json:"total_price"`
}
func main() {
o := Order{
Id: "1234",
Name: "learn go",
Quantity: 3,
TotalPrice: 30,
}
s, _ := json.Marshal(o)
fmt.Printf("%s\n", s) // {"id":"1234","name":"learn go","quantity":3,"total_price":30}
o2 := Order{
Id: "1234",
Name: "",
Quantity: 3,
TotalPrice: 30,
}
s2, _ := json.Marshal(o2)
fmt.Printf("%s\n", s2) // {"id":"1234","quantity":3,"total_price":30}
}
type OrderItem struct {
Id string `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
}
type Order struct {
Id string `json:"id"`
Item *OrderItem `json:"item"`
Quantity int `json:"quantity"`
TotalPrice float64 `json:"total_price"`
}
func main() {
o := Order{
Id: "1234",
Item: &OrderItem{
Id: "item_1",
Name: "learn go",
Price: 15,
},
Quantity: 3,
TotalPrice: 30,
}
s, _ := json.Marshal(o)
fmt.Printf("%s\n", s) // {"id":"1234","item":{"id":"item_1","name":"learn go","price":15},"quantity":3,"total_price":30}
}
type OrderItem struct {
Id string `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
}
type Order struct {
Id string `json:"id"`
Items []OrderItem `json:"items"`
TotalPrice float64 `json:"total_price"`
}
func main() {
marshal()
unmarshal()
}
func marshal() {
o := Order{
Id: "1234",
Items: []OrderItem{
{
Id: "item_1",
Name: "learn go",
Price: 15,
},
{
Id: "item_2",
Name: "interview",
Price: 10,
},
},
TotalPrice: 20,
}
s, _ := json.Marshal(o)
fmt.Printf("%s\n", s)
}
func unmarshal() {
s := `{"id":"1234","items":[{"id":"item_1","name":"learn go","price":15},{"id":"item_2","name":"interview","price":10}],"total_price":20}`
var o Order
json.Unmarshal([]byte(s), &o)
fmt.Printf("%+v\n", o)
}
func main() {
parseNLP()
}
func parseNLP() {
res := `{
"data": [
{
"synonym":"",
"weight":"0.6",
"word": "真丝",
"tag":"材质"
},
{
"synonym":"",
"weight":"0.8",
"word": "韩都衣舍",
"tag":"品牌"
},
{
"synonym":"连身裙;联衣裙",
"weight":"1.0",
"word": "连衣裙",
"tag":"品类"
}
]
}`
m := struct {
Data []struct {
Synonym string `json:"synonym"`
Tag string `json:"tag"`
} `json:"data"`
}{}
err := json.Unmarshal([]byte(res), &m)
if err != nil {
panic(err)
}
fmt.Printf("%+v, %+v\n", m.Data[2].Synonym, m.Data[2].Tag)
}
package main
import (
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"math/rand"
"time"
)
const keyRequestId = "requestId"
func main() {
r := gin.Default()
logger, err := zap.NewProduction()
if err != nil {
panic(err)
}
r.Use(func(c *gin.Context) {
s := time.Now()
c.Next()
// path, response code, log latency
logger.Info("incoming request",
zap.String("path", c.Request.URL.Path),
zap.Int("status", c.Writer.Status()),
zap.Duration("elapsed", time.Now().Sub(s)))
}, func(c *gin.Context) {
c.Set(keyRequestId, rand.Int())
c.Next()
})
r.GET("/ping", func(c *gin.Context) {
h := gin.H{
"message": "pong",
}
if rid, exists := c.Get(keyRequestId); exists {
h[keyRequestId] = rid
}
c.JSON(200, h)
})
r.GET("/hello", func(c *gin.Context) {
c.String(200, "hello")
})
r.Run()
}
对应课程: