go刷题专场
多协程执行后使用channel收集结果
low逼写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package main
import ( "fmt" "time" )
func job(index int) int { time.Sleep(time.Millisecond * 500) return index }
func main() { start := time.Now() num := 5 retCh := make(chan int) for i := 0; i < num; i++ { go func(param int) { retCh <- job(param) }(i) } count := 0 for item := range retCh { count++ fmt.Println("收到结果:", item) if count == num { close(retCh) break } }
end := time.Since(start) fmt.Println("耗时:", end.String()) }
|
优雅版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package main
import ( "fmt" "sync" "time" )
func job(index int) int { time.Sleep(time.Millisecond * 500) return index }
func main() { start := time.Now() num := 5 retCh := make(chan int) wg := sync.WaitGroup{} for i := 0; i < num; i++ { wg.Add(1) go func(param int) { defer wg.Done() retCh <- job(param) }(i) } go func() { defer close(retCh) wg.Wait() }() for item := range retCh { fmt.Println("收到结果:", item) }
end := time.Since(start) fmt.Println("耗时:", end.String()) }
|
实现一个简易的切片迭代器
简易版:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package main
import "fmt"
type Iterator struct { data []int index int }
func New(data []int) *Iterator { return &Iterator{data: data} }
func (i *Iterator) HasNext() bool { if i.data == nil || len(i.data) == 0 { return false } return i.index < len(i.data) }
func (i *Iterator) Next() int { defer func() { i.index++ }() return i.data[i.index] }
func main() { ints := []int{1, 2, 3, 4, 5} iter := New(ints) for iter.HasNext() { fmt.Println(iter.Next()) } }
|
支持多类型和封装的切片迭代器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| package main
import "fmt"
type Iterator struct { index int len int }
func (i *Iterator) HasNext() bool { if i.len == 0 { return false } return i.index < i.len }
type IntIterator struct { *Iterator data []int }
func IterForInt(data []int) *IntIterator { return &IntIterator{data: data, Iterator: &Iterator{len: len(data)}} }
func (i *IntIterator) Next() int { defer func() { i.index++ }() return i.data[i.index] }
type StringIterator struct { *Iterator data []string }
func IterForString(data []string) *StringIterator { return &StringIterator{data: data, Iterator: &Iterator{len: len(data)}} }
func (i *StringIterator) Next() string { defer func() { i.index++ }() return i.data[i.index] }
func main() { ints := []int{1, 2, 3, 4, 5} iter := IterForInt(ints) for iter.HasNext() { fmt.Println(iter.Next()) }
strs := []string{"a", "b", "c", "d"} iterStr := IterForString(strs) for iterStr.HasNext() { fmt.Println(iterStr.Next()) } }
|
模拟js的promise写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package main
import ( "fmt" "sync" "time" )
type Resolve func(rsp interface{})
type Reject func(err error)
type PromiseFunc func(resolve Resolve, reject Reject)
type Promise struct { f PromiseFunc resolve Resolve reject Reject wg sync.WaitGroup }
func NewPromise(f PromiseFunc) *Promise { return &Promise{f: f} }
func (p *Promise) Then(resolve Resolve) *Promise { p.resolve = resolve return p } func (p *Promise) Catch(reject Reject) *Promise { p.reject = reject return p }
func (p *Promise) Done() { p.wg.Add(1) go func() { defer p.wg.Done() p.f(p.resolve, p.reject) }() p.wg.Wait() }
func main() { NewPromise(func(resolve Resolve, reject Reject) { time.Sleep(time.Second * 1) if time.Now().Unix()%2 == 0 { resolve("OK") } else { reject(fmt.Errorf("error")) } }).Then(func(rsp interface{}) { fmt.Println(rsp) }).Catch(func(err error) { fmt.Println(err) }).Done() }
|
支持超时控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| package main
import ( "context" "fmt" "sync" "time" )
type Resolve func(rsp interface{})
type Reject func(err error)
type PromiseFunc func(resolve Resolve, reject Reject)
type PromiseOpt func(promise *Promise) type PromiseOpts []PromiseOpt
func (prom PromiseOpts) apply(p *Promise) { for _, opt := range prom { opt(p) } }
func WithTimeout(t time.Duration) PromiseOpt { return func(promise *Promise) { promise.timeout = t } }
type Promise struct { f PromiseFunc resolve Resolve reject Reject wg sync.WaitGroup timeout time.Duration }
func NewPromise(f PromiseFunc) *Promise { return &Promise{f: f, timeout: time.Second * 10} }
func (p *Promise) Then(resolve Resolve) *Promise { p.resolve = resolve return p } func (p *Promise) Catch(reject Reject) *Promise { p.reject = reject return p }
func (p *Promise) Done(opts ...PromiseOpt) { defer func() { if e := recover(); e != nil { p.reject(fmt.Errorf(e.(string))) } }() PromiseOpts(opts).apply(p) p.wg.Add(1) timeoutCtx, _ := context.WithTimeout(context.Background(), p.timeout) done := make(chan struct{}) go func() { defer p.wg.Done() p.f(p.resolve, p.reject) }() go func() { defer func() { done <- struct{}{} }() p.wg.Wait() }() select { case <-timeoutCtx.Done(): panic("超时") case <-done: fmt.Println("正常执行") } }
func main() { NewPromise(func(resolve Resolve, reject Reject) { time.Sleep(time.Second * 2) if time.Now().Unix()%2 == 0 { resolve("OK") } else { reject(fmt.Errorf("error")) } }).Then(func(rsp interface{}) { fmt.Println(rsp) }).Catch(func(err error) { fmt.Println("catch: ", err) }).Done(WithTimeout(time.Second * 1)) }
|
使用http分块发送实现网页倒计时
分块传输编码(Chunked transfer encoding)允许http由网页服务器发送给客户端应用(浏览器)的数据可以分成多个部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package main
import ( "fmt" "net/http" "time" )
const ( UpdateScript = `<script>document.getElementById("msg").innerHTML='%d';</script>` Redirect = `<script>self.location='https://setcreed.top'</script>` )
func main() { http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { h1 := []byte("<h1 id='msg'></h1>")
writer.Header().Set("Transfer-Encoding", "Chunked") writer.Write(h1) for i := 1; i <= 5; i++ { writer.Write([]byte(fmt.Sprintf(UpdateScript, i))) writer.(http.Flusher).Flush() time.Sleep(time.Second * 1) } writer.Write([]byte(Redirect)) writer.(http.Flusher).Flush() }) http.ListenAndServe(":8080", nil) }
|
Go中Struct的指针方法和非指针方法到底有啥区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import "fmt"
type User struct { Name string }
func (u User) Print1() { println("print1") }
func (u *User) Print2() { println("print2") }
func main() { fmt.Printf("%T\n", User.Print1) fmt.Printf("%T\n", User.Print2) u := User{} User.Print1(u) }
|
编译错误
1 2 3
| ❯ go run method.go # command-line-arguments ./method.go:19:25: invalid method expression User.Print2 (needs pointer receiver: (*User).Print2)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package main
import "fmt"
type User struct { Name string }
func (u User) Print1() { fmt.Printf("%p\n", &u) println("print1") }
func (u *User) Print2() { fmt.Printf("%p\n", u) println("print2") }
func main() { u := User{} fmt.Printf("%p\n", &u) User.Print1(u)
var u2 *User (*User).Print2(u2) }
|
1 2 3 4 5 6
| ❯ go run method.go 0xc000010230 0xc000010240 print1 0x0 print2
|
使用channel读取文件时在控制台显示进度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package main
import ( "fmt" "io" "io/ioutil" "os" "time" )
const ( Format1 = "\b\b%d%%" Format2 = "\b\b\b%d%%" Format3 = "\b\b\b%d%%\b" )
func progress(ch chan int64) { format := Format1 var lastNum int64 = 0 for rate := range ch { if lastNum > 10 && rate > 10 && rate < 100 { format = Format2 } else if rate >= 100 { rate = 100 format = Format3 } fmt.Printf(format, rate) lastNum = rate } }
func main() { rateCh := make(chan int64) defer close(rateCh) f, _ := os.Open("./html/img.jpg") fInfo, _ := f.Stat() fmt.Print("rate:0%") go progress(rateCh)
ret := make([]byte, 0) for { buf := make([]byte, 1024*5) n, err := f.Read(buf) if err != nil && err != io.EOF { panic(err) } if n == 0 { break } ret = append(ret, buf...) time.Sleep(time.Second) go func() { rateCh <- int64(len(ret)*100) / fInfo.Size() }() } ioutil.WriteFile("./ret.png", ret, 0600) }
|
使用Gin模拟大文件上传、分片存储和分块显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| package main
import ( "fmt" "io" "io/ioutil" "net/http" "os" "time"
"github.com/gin-gonic/gin" )
func saveBlock(name string, buf []byte) { save, _ := os.OpenFile("./file/"+name, os.O_CREATE|os.O_RDWR, 0600) defer save.Close() save.Write(buf) }
func main() { r := gin.New() r.Use(func(c *gin.Context) { defer func() { if e := recover(); e != nil { c.AbortWithStatusJSON(400, gin.H{"err": e}) } }() c.Next() }) r.GET("/", func(c *gin.Context) { c.Writer.Header().Set("Transfer-Encoding", "chunked") c.Writer.Header().Set("Content-Type", "image/png") for i := 0; i <= 5; i++ { f, _ := os.Open(fmt.Sprintf("./file/img_%d.png", i)) time.Sleep(time.Second) b, _ := ioutil.ReadAll(f) c.Writer.Write(b) c.Writer.(http.Flusher).Flush() } }) r.POST("file", func(c *gin.Context) { file, head, _ := c.Request.FormFile("file") block := head.Size / 5 index := 0 for { buf := make([]byte, block) n, err := file.Read(buf) if err != nil && err != io.EOF { panic(err) } if n == 0 { break } time.Sleep(time.Second) saveBlock(fmt.Sprintf("img_%d.png", index), buf) index++ } c.JSON(200, gin.H{"message": "OK"}) }) r.Run(":8080") }
|
String转[]byte的两种方法
第一种:
1 2 3 4
| func main() { str := "abc" fmt.Println([]byte(str)) }
|
1 2 3 4
| func main() { a := "abc" fmt.Println((*reflect.StringHeader)(unsafe.Pointer(&a)).Data) }
|
Pointer: 表示可以指向任意类型的指针
uintptr:无符号整型,可以用来保存一个指针地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import ( "fmt" "reflect" "unsafe" )
func main() { str := "adc" strHeader := (*reflect.StringHeader)(unsafe.Pointer(&str))
var strBytes []byte bytesHeader := (*reflect.SliceHeader)(unsafe.Pointer(&strBytes)) bytesHeader.Data = strHeader.Data bytesHeader.Len = strHeader.Len bytesHeader.Cap = strHeader.Len
fmt.Println(*(*[]byte)(unsafe.Pointer(bytesHeader))) }
|
超时控制带来的内存泄露
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package main
import ( "context" "fmt" "runtime" "time" )
func job() error { ctx, _ := context.WithTimeout(context.Background(), time.Second*1) done := make(chan struct{}) go func() { time.Sleep(time.Millisecond * 1500) done <- struct{}{} }() select { case <-done: return nil case <-ctx.Done(): return fmt.Errorf("超时了") } }
func main() { for i := 0; i <= 50; i++ { go func() { job() }() } for { time.Sleep(time.Second * 2) fmt.Println(runtime.NumGoroutine()) } }
|
context上手
上下文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package main
import ( "context" "fmt" "time" )
func g(ctx context.Context) { fmt.Println(ctx.Value("begin")) fmt.Println("你是猪~")
go g2(context.WithValue(ctx, "movie", "复仇者联盟")) }
func g2(ctx context.Context) { fmt.Println(ctx.Value("movie")) fmt.Println("电影很好看") }
func main() { ctx := context.WithValue(context.Background(), "begin", "从台词看到一句话:") go g(ctx) time.Sleep(time.Second) }
|
cancel取消
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package main
import ( "context" "fmt" "time" )
func g1(ctx context.Context) { go g2(ctx) select { case <-ctx.Done(): fmt.Println("g1取消了") return } }
func g2(ctx context.Context) { select { case <-ctx.Done(): fmt.Println("g2取消了") return } }
func main() { ctx, cancel := context.WithCancel(context.Background()) go g1(ctx) count := 1 for { if count >= 3 { cancel() } time.Sleep(time.Second) count++ } }
|
超时和取消
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package main
import ( "context" "fmt" "log" "time" )
func g1(ctx context.Context) { done := make(chan struct{}) go func() { time.Sleep(time.Second * 1) done <- struct{}{} }() select { case <-ctx.Done(): fmt.Println("g1被取消或超时") return case <-done: fmt.Println("正常执行完成") return } }
func main() { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500) go g1(ctx) count := 1 for { if count >= 3 { log.Println("手动取消") cancel() count = 0 } time.Sleep(time.Second) if count > 0 { count++ } } }
|