关于golang:一文详读-Go-By-Example

7次阅读

共计 10924 个字符,预计需要花费 28 分钟才能阅读完成。

通道同步

func worker(done chan bool) {fmt.Println("working...")
    time.Sleep(2 * time.Second)
    fmt.Println("done")

    done <- true
}

func main() {done := make(chan bool, 1)
    go worker(done)
    <-done
}

后果:

working...
done
[Finished in 3.1s]

通道抉择

import (
    "fmt"
    "time"
)

func main() {ch1 := make(chan string, 1)
    ch2 := make(chan string, 2)

    go func() {time.Sleep(time.Second * 2)
        ch1 <- "one"
    }()

    go func() {time.Sleep(time.Second)
        ch2 <- "two"
    }()

    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
}

后果:

$ go run  main.go
two

超时解决

func main() {ch1 := make(chan string, 1)

    go func() {time.Sleep(time.Second * 1)
        ch1 <- "1"
    }()

    select {
    case res := <-ch1:
        fmt.Println(res)
    case <-time.After(time.Second * 1):
        fmt.Println("time out 1")
    }

    ch2 := make(chan string, 1)
    go func() {time.Sleep(time.Second * 2)
        ch2 <- "2"
    }()

    select {
    case res := <-ch1:
        fmt.Println(res)
    case <-time.After(time.Second * 1):
        fmt.Println("time out 2")
    }
}

后果:

$ go run  main.go
1
time out 1

非阻塞通道

func main() {message := make(chan string)

    select {
    case msg := <-message:
        fmt.Print("message", msg)
    default:
        fmt.Println("no message receive")
    }

    msg := "1"
    select {
    case message <- msg: // 当 message 通道定义一个缓冲区的时候,这里能够执行
        fmt.Println("sent message")
    default:
        fmt.Println("no message sent")
    }
}

后果

$ go run  main.go
no message receive
no message sent

通道敞开

func main() {jobs := make(chan int, 5)
    done := make(chan bool)

    go func() {
        for {
            j, ok := <-jobs
            if ok {fmt.Println("receive job", j)
            } else {fmt.Println("receive all jobs")
                done <- true      // 告诉主程序,曾经承受全副工作
                return
            }
        }
    }()

    for i := 1; i < 3; i++ {
        jobs <- i
        fmt.Println("send job", i)
    }

    close(jobs)
    fmt.Println("send all jobs")

    <-done  // 期待告诉
}

后果

$ go run main.go
send job 1
send job 2
send all jobs
receive job  1
receive job  2
receive all jobs

遍历通道

func main() {queue := make(chan string, 3)

    queue <- "one"
    queue <- "two"

    close(queue)

    for elem := range queue {fmt.Println(elem)
    }
}

后果:

$ go run main.go
one
two

定时器

func main() {timer1 := time.NewTimer(time.Second * 2)
    <-timer1.C
    fmt.Println("timer 1 expired")

    timer2 := time.NewTimer(time.Second * 2)

    <-timer2.C
    fmt.Println("timer 2 expired")

    stop2 := timer2.Stop() // 此时 timer2 曾经倒计时完结了,所以不须要进行
    fmt.Println("stop2:", stop2)
    if stop2 {fmt.Println("timer 2 stoped")
    }
}

后果:

$ go run main.go
timer 1 expired
timer 2 expired
stop2: false

下面例子中,因为 timer2 的倒计时曾经进行,timer2.stop() 没有执行,返回为 false,如果想看进行成果,能够改写代码:

func main() {timer1 := time.NewTimer(time.Second * 2)
    <-timer1.C
    fmt.Println("timer 1 expired")

    timer2 := time.NewTimer(time.Second * 5)
    go func() {
        <-timer2.C
        fmt.Println("timer 2 expired")
    }()

    stop2 := timer2.Stop() 
    fmt.Println("stop2:", stop2)
    if stop2 {fmt.Println("timer 2 stoped")
    }
}

后果:

$ go run main.go
timer 1 expired
stop2: true
timer 2 stoped

能够看到 stop2 进行了计时器,程序间接退出了。

也能够应用 time 自带的 after 办法实现

func main() {ch := make(chan string)

    go func() {time.Sleep(time.Second * 2)

        ch <- "result"
    }()

    select {
    case res := <-ch:
        fmt.Println(res)
    case <-time.After(time.Second * 1):
        fmt.Println("timeout")
    }
}

后果:

$ go run main.go
timeout

计时器

Ticker 和 timer 的区别是,timer 倒计时到某一个工夫点发送一个信号,而 ticker 是每隔多长时间发送一个信息,直到咱们手动 stop

func main() {ticker := time.NewTicker(time.Second)

    go func() {
        for t := range ticker.C {fmt.Println("Tick at", t)
        }
    }()

    time.Sleep(time.Second * 5)
    ticker.Stop()

    fmt.Println("ticker stopped")
}

后果:

$ go run main.go
Tick at  2021-05-20 08:55:17.817703 +0800 CST m=+1.003478727
Tick at  2021-05-20 08:55:18.819047 +0800 CST m=+2.004844288
Tick at  2021-05-20 08:55:19.814649 +0800 CST m=+3.000467753
Tick at  2021-05-20 08:55:20.81894 +0800 CST m=+4.004780216
ticker stopped
Tick at  2021-05-20 08:55:21.815348 +0800 CST m=+5.001210115

后果是每隔 1 秒将以后工夫作为值 push 到通道,而后咱们循环这个通道获取值。通过源码能够看到 Ticker.C 是一个 time 类型的 channel。

源码:

type Ticker struct {
    C <-chan Time // The channel on which the ticks are delivered.
    r runtimeTimer
}

工作池

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {fmt.Println("worker", id, "process job", j)
        time.Sleep(time.Second * 2)
        results <- j 
    }
}

func main() {jobs := make(chan int, 100)
    results := make(chan int, 100)

    // 开启 5 个过程
    for w := 1; w <= 5; w++ {go worker(w, jobs, results)
    }

    // 向通道 push 工作
    for j := 1; j <= 9; j++ {jobs <- j}
    
    close(jobs)
    
    for r := 1; r <= 9; r++ {<-results}
}

result 作用是告知主过程执行完结,当所有的执行完结后,主过程完结退出工作,如果没有 result 可能会导致子过程还没有完结,主过程就退出了。
后果

worker 3 process id 1
worker 1 process id 2
worker 2 process id 3
worker 1 process id 4
worker 2 process id 6
worker 3 process id 5
worker 2 process id 8
worker 1 process id 9
worker 3 process id 7

限速

func main() {

    // 限度每 2 秒执行一次申请
    ticker := time.Tick(time.Second * 2)
    for i := 1; i <= 5; i++ {
        <-ticker
        fmt.Println("request", i, time.Now())
    }

    // 先向 burstylimiter push 3 个值
    limiter := make(chan time.Time, 3)
    for i := 0; i < 3; i++ {limiter <- time.Now()
    }

    // 而后开启另外一个线程,每 2 秒向 burstylimiter push 一个值
    go func() {for t := range time.Tick(time.Second * 2) {limiter <- t}
    }()

    // 最初实现成果,前三次没有限速,最初两次每 2 秒执行一次
    for i := 1; i <= 5; i++ {
        <-limiter
        fmt.Println("request", i, time.Now())
    }

}

后果:

request 1 2021-05-20 10:09:01.121992 +0800 CST m=+2.005258478
request 2 2021-05-20 10:09:03.117609 +0800 CST m=+4.000918022
request 3 2021-05-20 10:09:05.116884 +0800 CST m=+6.000235109
request 4 2021-05-20 10:09:07.11969 +0800 CST m=+8.003084206
request 5 2021-05-20 10:09:09.119841 +0800 CST m=+10.003278026
request 1 2021-05-20 10:09:09.119978 +0800 CST m=+10.003414895
request 2 2021-05-20 10:09:09.120101 +0800 CST m=+10.003538622
request 3 2021-05-20 10:09:09.12018 +0800 CST m=+10.003616297
request 4 2021-05-20 10:12:29.322124 +0800 CST m=+12.005434486
request 5 2021-05-20 10:12:31.322453 +0800 CST m=+14.005806367

成果:前 5 次,距离 2s,第 6 - 8 次,不间隔时间,9-10 次再次距离 2s 执行。

互斥锁

func main() {var state = make(map[int]int)
    var mutex = &sync.Mutex{}

    for w := 0; w < 10; w++ {go func() {
            for {key := rand.Intn(5)
                val := rand.Intn(100)
                mutex.Lock()           // 加锁
                state[key] = val
                mutex.Unlock()         // 解锁}
        }()}

    time.Sleep(time.Second)

    fmt.Println("state:", state)

}

后果:

$ go run main.go
state: map[0:72 1:25 2:36 3:44 4:38]

当去掉互斥锁配置当前,代码报错,因为同时读写一块内存地址。

go 状态协程

func main() {reads := make(chan *readOp)
    writes := make(chan *writeOp)

    // 通过 select 抉择来保障同时只能读或写操作
    go func() {var state = make(map[int]int)
        for {
            select {
            case read := <-reads:
                read.resp <- state[read.key]
            case writes := <-writes:
                state[writes.key] = writes.val
                writes.resp <- true
            }
        }
    }()

    for r := 0; r < 100; r++ {go func() {
            for true {
                read := &readOp{key:  rand.Intn(5),
                    resp: make(chan int),
                }
                reads <- read
            }
        }()}

    for w := 0; w < 10; w++ {go func() {
            for {
                write := &writeOp{key:  rand.Intn(5),
                    val:  rand.Intn(100),
                    resp: make(chan bool),
                }

                writes <- write
                <-write.resp
            }
        }()}

    time.Sleep(time.Second)
}

这里通过 select 抉择来保障同时只有一个读或写操作,这样比通过互斥锁简单。

排序

func main() {strs := []string{"c", "a", "b"}
    sort.Strings(strs)
    fmt.Println("Strings:", strs)

    ints := []int{1, 6, 3, 5, 2}
    sort.Ints(ints)
    fmt.Println("Ints:", ints)
    // 判断是否排序
    s := sort.IntsAreSorted(ints)
    fmt.Println("Sorted:", s)
}

自定义规定排序

以字符串长度排序

type ByLength []string

func (b ByLength) Len() int {return len(b)
}

func (b ByLength) Swap(i, j int) {b[i], b[j] = b[j], b[i]
}

func (b ByLength) Less(i, j int) bool {return len(b[i]) < len(b[j])
}

func main() {fruits := []string{"apple", "banana", "kiwi", "orage"}
    sort.Sort(ByLength(fruits))
    fmt.Println(fruits)
}

后果

$ go run main.go
[kiwi apple orage banana]

正则表达式

func main() {match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
    fmt.Println(match)

    r, _ := regexp.Compile("p([a-z]+)ch")

    fmt.Println(r.MatchString("peach"))
    fmt.Println(r.Match([]byte("peach")))

    fmt.Println(r.FindString("peach punch"))
    fmt.Println(r.FindStringIndex("peach punch"))
    fmt.Println(r.FindStringSubmatch("peach punch"))
    fmt.Println(r.FindStringSubmatchIndex("peach punch"))

    fmt.Println(r.FindAllString("peach punch pinch", -1))
    fmt.Println(r.FindAllString("peach punch pinch", 2))
    fmt.Println(r.FindAllString("peach punch pinch", 1))
    fmt.Println(r.FindAllStringSubmatch("peach pinch punch", -1))
    fmt.Println(r.FindAllStringSubmatchIndex("peach pinch punch", -1))

    r = regexp.MustCompile("p([a-z]+)ch")
    fmt.Println(r)
    fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))

    in := []byte("a peach")
    out := r.ReplaceAllFunc(in, bytes.ToUpper)
    fmt.Println(string(out))
}

后果:

true
true
true
peach
[0 5]
[peach ea]
[0 5 1 3]
[peach punch pinch]
[peach punch]
[peach]
[[peach ea] [pinch in] [punch un]]
[[0 5 1 3] [6 11 7 9] [12 17 13 15]]
p([a-z]+)ch
a <fruit>
a PEACH

工夫戳

func main() {now := time.Now()
    secs := now.Unix()
    nanos := now.UnixNano()

    fmt.Println(now)
    fmt.Println(secs)
    fmt.Println(nanos)

    fmt.Println(time.Unix(secs, 0))
    fmt.Println(time.Unix(0, nanos))
}

后果:

2021-05-21 09:19:47.347155 +0800 CST m=+0.000135175
1621559987
1621559987347155000
2021-05-21 09:19:47 +0800 CST
2021-05-21 09:19:47.347155 +0800 CST

数字解析

将字符串解析成对应的数字类型,当遇到无奈解析时,会报错。

func main() {f, _ := strconv.ParseFloat("1.234", 64)
    fmt.Println(f)

    i, _ := strconv.ParseInt("1234", 0, 64)
    fmt.Println(i)

    k, _ := strconv.Atoi("135")
    fmt.Println(k)

    _, e := strconv.Atoi("wat")
    fmt.Println(e)
}

后果:

1.234
1234
135
strconv.Atoi: parsing "wat": invalid syntax

解析 URL 地址

func main() {
    s := "postgres://user:pass@host.com:5432/path?k=v#f"

    u, err := url.Parse(s)
    if err != nil {panic(err)
    }

    fmt.Println(u.User.Username())
    p, _ := u.User.Password()
    fmt.Println(p)

    fmt.Println(u.Host)
    fmt.Println(strings.Split(u.Host, ":")[0])
    fmt.Println(strings.Split(u.Host, ":")[1])

    // 获取门路
    fmt.Println(u.Path)

    // 获取参数值
    fmt.Println(u.Fragment)
    fmt.Println(u.RawQuery)
}

后果:

$ go run main.go
user
pass
host.com:5432
host.com
5432
/path
f
k=v

SHA1 散列

func main() {
    s := "sha1 shis string"

    // 创立一个 sha1 对象
    h := sha1.New()

    // 对字符串解决
    h.Write([]byte(s))

    // sumk 能够用来对现有字符串切片追加额定字节,个别不须要
    bs := h.Sum(nil)

    fmt.Println(s)
    fmt.Printf("%x\n", bs)
}

后果:

sha1 shis string
6bf8cad402882fb0fc2aed041dcc79b8e686cfc6

Scan 过滤

func main() {
    // 从命令行获取数据
    scanner := bufio.NewScanner(os.Stdin)

    for scanner.Scan() {ucl := strings.ToUpper(scanner.Text())
        fmt.Println(ucl)
    }

    if err := scanner.Err(); err != nil {fmt.Fprintln(os.Stderr, "error", err)
        os.Exit(1)
    }
}

后果:

$ echo "nihaoya" |go run main.go
NIHAOYA

命令行参数

func main() {
    argsWithProg := os.Args
    argsWithoutProg := os.Args[1:]
    arg := os.Args[3]

    fmt.Println(argsWithProg)
    fmt.Println(argsWithoutProg)
    fmt.Println(arg)
}

后果:

[/var/folders/p0/cv96ln_j6t7d6g_lwfwcqgqc0000gn/T/go-build765472776/b001/exe/main a b c d]
[a b c d]
c

环境变量

func main() {os.Setenv("FOO", "1")
    fmt.Println("FOO:", os.Getenv("FOO"))
    fmt.Println("BAR:", os.Getenv("BAR"))

    for _, e := range os.Environ() {pair := strings.Split(e, "=")
        fmt.Println(pair[0])
    }
}

后果:

FOO: 1
BAR: 
__INTELLIJ_COMMAND_HISTFILE__
HOME
__CF_USER_TEXT_ENCODING
LOGIN_SHELL
PATH
LC_CTYPE
USER
SSH_AUTH_SOCK
TMPDIR
SHELL
LOGNAME
XPC_SERVICE_NAME
GO111MODULE
GOPATH
XPC_FLAGS
GOROOT

执行系统命令

func main() {
    // 执行工夫命令
    dateCmd := exec.Command("date")
    dateOut, err := dateCmd.Output()
    if err != nil {panic(err)
    }
    fmt.Println(string(dateOut))

    // 执行 ls 命令
    lsCmd := exec.Command("bash", "-c", "ls -a -l -h")
    lsOut, err := lsCmd.Output()
    if err != nil {panic(err)
    }
    fmt.Println(string(lsOut))

    // 执行 grep 命令
    grepCmd := exec.Command("grep", "hello")

    // 获取输出和输入对象
    grepIn, _ := grepCmd.StdinPipe()
    grepOut, _ := grepCmd.StdoutPipe()

    // 开始执行
    grepCmd.Start()

    // 输出字符
    grepIn.Write([]byte("hello grep\ngoodbye grep"))
    grepIn.Close()

    // 读取输入
    grepBytes, _ := ioutil.ReadAll(grepOut)
    grepCmd.Wait()
    fmt.Println("> grep hello")
    fmt.Println(string(grepBytes))
}

后果:

$ go run main.go
Fri May 21 11:33:32 CST 2021

total 160
drwxr-xr-x  17 liangkai  admin   544B May 21 11:33 .
drwxr-xr-x@ 21 liangkai  admin   672B May 17 17:40 ..
-rw-r--r--@  1 liangkai  admin    10K May 17 18:48 .DS_Store
-rw-r--r--   1 liangkai  admin    67B Dec 21 12:08 .env
drwxr-xr-x   8 liangkai  admin   256B May 19 17:47 .idea
drwxr-xr-x   3 liangkai  admin    96B Apr 30 08:13 .vscode
drwxr-xr-x   4 liangkai  admin   128B Apr 30 15:37 Other
drwxr-xr-x   4 liangkai  admin   128B Dec 21 09:13 PracChan
drwxr-xr-x   4 liangkai  admin   128B Apr 12 16:54 PracCodeSvn
drwxr-xr-x   3 liangkai  admin    96B Mar  3 10:59 PracExporter
drwxr-xr-x   7 liangkai  admin   224B Apr 30 15:37 PracHttp
drwxr-xr-x   5 liangkai  admin   160B Dec 21 09:10 PracInterface
drwxr-xr-x   6 liangkai  admin   192B Apr 30 14:46 base
-rw-r--r--   1 liangkai  admin   147B Feb 20 18:44 config.ini
-rw-r--r--   1 liangkai  admin   798B Apr 30 15:41 go.mod
-rw-r--r--   1 liangkai  admin    52K Apr 30 15:41 go.sum
-rw-r--r--@  1 liangkai  admin   725B May 21 11:33 main.go

> grep hello
hello grep

零碎信号

func main() {sigs := make(chan os.Signal, 1)
    done := make(chan bool, 1)

    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

    go func() {
        sig := <-sigs
        fmt.Println()
        fmt.Println(sig)
        done <- true
    }()

    fmt.Println("waitting signal")
    <-done
    fmt.Println("exiting")

}

后果:

$ go run main.go
waitting signal


^C
interrupt
exiting
正文完
 0