reference from: http://blog.wuxu92.com/

Table of Contents

  1. 不定參數
  2. closure
  3. Error handle
  4. channel
    1. 使用channel同步goroutine
    2. channel directive
    3. select
    4. 超時
    5. non-blocking channel
    6. range over channel
  5. Timer & Ticker
    1. Timer
    2. Tickers
    3. worker pool/worker池
    4. rate-limiting
  6. rand
  7. strconv
  8. url parse
  9. A Full Example For Polymorphism & Composition

不定參數

1
2
3
4
5
6
7
8
9
func sum (nums ...int){
total := 0
for _, num := range nums {
total += num
}
return total
}
nums := []int{1,2,3,4,5}
t := sum(nums...)

closure

1
2
3
4
5
6
7
8
9
10
func intSeq() func() int {
i := 0
return func() int {
i += 1;
return i
}
}
next := intSeq()
// next是一个命名函数了
next()

Error handle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type myErr struct {
arg int
prob string
}
func (e myErr) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
type Person struct{
age int
}
func (p Person)buy(item string) (bool, error) {
if p.age < 18 {
return false, &myErr{0, "too yong"}
}
return true, nil
}
me := Person{10}
, e := me.buy("shirt")
if me, ok := e.(*myErr); ok {
// with myErr return, me ref to return error
...
}

channel

channel預設是blocking的
channel中沒東西recieve的話會block, 若已沒有其他goroutine則會panic

使用channel同步goroutine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func working(flagChan chan bool) {
fmt.Println("working on it")
time.Sleep(2 * time.Second)
fmt.Println("work done")
flagChan <- true
}
func chanSync() {
flagChan := make(chan bool, 1)
go working(flagChan)
// 下面的接受操作会阻塞等待goroutine执行结束
done := <- flagChan
fmt.Println("sync goroutine output", done)
}

channel directive

1
2
func send (flagChan chan<- bool) { ... }
func recv (flagChan <-chan bool) { ... }

select

select用於從多個channel中輪訓狀態, 從已經準備就緒的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
import (
"math/rand"
"time"
"strconv"
)
func gr1(c chan string) {
dur := rand.Intn(300)
time.Sleep(time.Millisecond * time.Duration(dur))
c <- "gr1 sleep done " + strconv.Itoa(dur)
}
func gr2(c chan string) {
dur := rand.Intn(400)
time.Sleep(time.Millisecond * time.Duration(dur))
c <- "gr2 sleep done " + strconv.Itoa(dur)
}
func selects() {
c1 := make(chan string)
c2 := make(chan string)
go gr1(c1)
go gr1(c1)
go gr2(c2)
go gr2(c2)
for i:=0; i<4; i++ {
select {
case msg := <-c1:
fmt.Println(msg)
case msg2 := <-c2:
fmt.Println(msg2)
}
}
}

超時

若等待超過一定時間沒有resource則執行

1
2
3
4
5
6
select {
case res <- c1 :
fmt.Println(res)
case <-time.After(time.Seconde * 2)
fmt.Println("timeout")
}

non-blocking channel

所有case的channel都没有就绪的时候执行default

1
2
3
4
5
6
7
8
9
select {
case msg := <-c1:
fmt.Println(msg)
case msg2 := <-c2:
fmt.Println(msg2)
default:
// non-blocking channel
fmt.Println("select default")
}

range over channel

对于一个close了得channel,range能自动判断是否遍历结束,但是如果channel没有调用过close,在下面的代码中,会在接受第三个值的时候阻塞(异常退出)

1
2
3
4
5
6
7
8
9
func rangeChannel() {
c := make(chan string, 2)
c <- "tomorrow is monday"
c <- "fee out"
close(c)
for str := range c {
fmt.Println(str)
}
}

Timer & Ticker

Timer

timer is like a channel

1
2
3
4
5
6
import "time"
...
// 定义一个timer,超时设定2秒
timer1 := time.NewTimer(time.Second *2)
// 在此等待2s
<- timer1.C //注意.C

Tickers

ticker就像打点器一样循环执行某代码
ticker看作一个channel,在初始化时定义一个循环时间,每过一段时间往channel里面塞一个数,然后我们循环去取

1
2
3
4
5
6
7
8
9
t1 := time.NewTicker(time.Millisecond * 500)
go func() {
for t := range t1.C {
fmt.Println("tick tick @ ", t)
}
}()
time.Sleep(time.Second * 3)
t1.Stop()
fmt.Println("tick tick stop")

worker pool/worker池

可以把一个goroutine看作一个worker。给多个worker传入相同的channel,worker从channel中取任务并处理,主线程往channel中添加任务,这样过个goroutine处理一个任务队列。

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
jobs := make(chan int, 100)
result := make(chan int, 100)
// use five workers
for w:=0; w<5; w++ {
go func(w int, jobs <-chan int, result chan<- int) {
for j:= range jobs {
fmt.Println("worker", w, "processing on", j)
time.Sleep(time.Millisecond * time.Duration(rand.Intn(600))) // sleep a random time
result <- j * 2
}
}(w, jobs, result)
}
// insert jobs
for j:=0; j<10; j++ {
jobs <- j
}
close(jobs)
// can do some others here
// sync here, may wait for works done
for a :=0; a<10; a++ {
<- result
}
fmt.Println("all work done here")

rate-limiting

使用goroutine,channel, ticker合作实现该特性
使用两个channel协同,一个用于处理速率的控制,一个用于请求的缓冲。使用一个goroutine不断的定时填充速率控制goroutine,而请求channel则用一个for-range不停行从里面取请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
rateControlChan := make(chan time.Time, 3) // 用于控制速率的channel
for i:=0; i<3; i++ {
rateControlChan <- time.Now()
}
// send value per 200 millisecond
go func() {
for t := range time.Tick(time.Millisecond * 200) {
rateControlChan <- t
}
}()
reqChannel := make(chan int, 5) // 缓存请求的channel
for i:=0; i< 5; i++ {
reqChannel <- i
}
close(reqChannel)
for r:= range reqChannel {
ht := <- rateControlChan // wait for rate-control condition
// handle request here
fmt.Println("req", r, "handle at", ht)
}

rand

1
2
3
4
5
6
7
8
rs := rand.NewSource(time.Now().UnixNano())
r := rand.New(rs)
// r此时是一个新的随机数生成器
// 正式使用应该这样
r.Intn(100)
rand.Float64() // 返回0-1之间的一个浮点数
rand.Int31()
rand.Uint32()

strconv

1
2
3
4
5
6
7
8
9
10
// parseFloat原型
func ParseFloat(s string, bitSize int) (f float64, err error)
f,_ := strconv.ParseFloat("1.23", 64) // 转换到64位Float,不处理转换的错误
// parseInt的原型, base从2-36
func ParseInt(s string, base int, bitSize int) (i int64, err error)
// 更常用的
k,_ := strconv.Atoi("123") // k=123
_,e := strconv.Atoi("wrongFormat") // will return error

url parse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import "net/url"
urlStr := "https://gobyexample.com/url-parsing?from=goole#url-parsing"
u, err := url.Parse(urlStr)
if err != nil { fmt.Println(err)
}
fmt.Println(u.Scheme,u.Host, u.Path, u.Fragment, u.RawQuery)
host, port, _ := net.SplitHostPort(u.Host)
user := u.User
username := "null"
if user != nil {
username = user.Username() // user.Password()
}
fmt.Println("username", username)
// 解析queryString
query, _ := url.ParseQuery(u.RawQuery)
fmt.Println(query, query["from"][0])

A Full Example For Polymorphism & Composition

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
// Sample program to show what happens when the outer and inner
// type implement the same interface.
package main
import (
"fmt"
)
// notifier is an interface that defined notification
// type behavior.
type notifier interface {
notify()
}
// user defines a user in the program.
type user struct {
name string
email string
}
// admin represents an admin user with privileges.
type admin struct {
user
level string
}
// notify implements a method that can be called via
// a value of type user.
func (u *user) notify() {
fmt.Printf("user: Sending user email To %s<%s>\n",
u.name,
u.email)
}
// notify implements a method that can be called via
// a value of type Admin.
func (a *admin) notify() {
fmt.Printf("User: Sending Admin Email To %s<%s>\n",
a.name,
a.email)
}
// main is the entry point for the application.
func main() {
// Create an admin user.
ad := admin{
user: user{
name: "john smith",
email: "john@yahoo.com",
},
level: "super",
}
// Send the admin user a notification.
// The embedded inner type's implementation of the
// interface is NOT "promoted" to the outer type.
sendNotification(&ad)
// We can access the inner type's method directly.
ad.user.notify()
// The inner type's method is NOT promoted.
ad.notify()
}
// sendNotification accepts values that implement the notifier
// interface and sends notifications.
func sendNotification(n notifier) {
n.notify()
}