专栏名称: 蒸汽的兔子
运维开发工程师
今天看啥  ›  专栏  ›  蒸汽的兔子

一文详读 Go By Example

蒸汽的兔子  · 掘金  ·  · 2021-05-25 08:35

文章预览

阅读 57

一文详读 Go By Example

通道同步

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

复制代码
………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览