- 通道同步
- 通道選擇
- 逾時處理
- 非阻塞通道
- 通道關閉
- 周遊通道
- 定時器
- 計時器
- 工作池
- 限速
- 互斥鎖
- go狀态協程
- 排序
- 自定義規則排序
- 正規表達式
- 時間戳
- 數字解析
- 解析URL位址
- SHA1散列
- Scan過濾
- 指令行參數
- 環境變量
- 執行系統指令
- 系統信号
通道同步
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:[email protected]: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