未经审视的生活是不值得过的 - 苏格拉底

生命是一团欲望,欲望不能满足便痛苦,满足便无聊,人生就在痛苦和无聊之间摇摆 - 叔本华

上周五刚卷铺盖滚出了入职不满四个多月的厂子,如今写起字来不免悲凉,只希望接下来文思泉涌时莫泪洒键盘,笔记本给整坏了,恐怕一时半会都没钱修理了。


干过的事儿

阅读全文 »

翻译自 https://gobyexample.com/

Go by Example

Go is an open source programming language designed for building simple, fast, and reliable software.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Go by Example: Stateful Goroutines

未完待续…

翻译自 https://gobyexample.com/

Go by Example

Go is an open source programming language designed for building simple, fast, and reliable software.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Go by Example: Mutexes

在前面的示例中我们看到如何通过原子操作来管理简单的计数器。对于更复杂的状态,我们可以使用互斥锁在多个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
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
package main

import (
"fmt"
"math/rand"
"sync"
"sync/atomic"
"time"
)

func main() {
// 此处状态是一个map
var state = make(map[int]int)
// 初始化锁
var mutex = &sync.Mutex{}
// 定义两个变量用于追踪读和写的总次数
var readOps uint64 = 0
var writeOps uint64 = 0

// 启动100个goroutines读取状态值,每个goroutine耗时1毫秒
for r := 0; r < 100; r++ {
go func() {
total := 0
for {
// 随机读取状态的某一个值
// 1、加锁
// 2、读取
// 3、解锁
key := rand.Intn(5)
mutex.Lock()
total += state[key]
mutex.Unlock()
// 次数自增1
atomic.AddUint64(&readOps, 1)

// 两次读取之间睡眠1毫秒
time.Sleep(time.Millisecond)
}
}()
}

// 同上,启动10个goroutines模拟写操作
// 随机给状态赋值,写操作次数自增1,睡眠1毫秒
for w := 0; w < 10; w++ {
go func() {
for {
key := rand.Intn(5)
val := rand.Intn(100)
mutex.Lock()
state[key] = val
mutex.Unlock()
atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}

// 睡眠一秒等待读写操作
// 打印出读写次数
time.Sleep(time.Second)
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)

// 使用锁取出最后的状态值
mutex.Lock()
fmt.Println("state:", state)
mutex.Unlock()
}
阅读全文 »

翻译自 https://gobyexample.com/

Go by Example

Go is an open source programming language designed for building simple, fast, and reliable software.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Go by Example: Atomic Counters

golang管理状态的主要机制是通过channel的通信,可以通过之前的worker pools的例子看到。还有一些其他的管理状态的方式,这次我们探究一下sync/atomic包中的可以被多个goroutine访问的atomic counters.

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
package main

import "fmt"
import "time"
import "sync/atomic"

func main() {
// 使用无符号正整数用来表示计数
var ops uint64 = 0

// 为了模拟并发更新,我们启动50个goroutine,每个goroutine对计数自增1,大概1毫秒的耗时
for i := 0; i < 50; i++ {
go func() {
for {
// 使用AddUint64进行原子性的递增操作,将变量的地址作为参数传给该函数
atomic.AddUint64(&ops, 1)

// 睡眠1毫秒,模拟耗时
time.Sleep(time.Millisecond)
}
}()
}

// 睡眠1秒进行若干累加操作
time.Sleep(time.Second)

// 由于计数器可能正在被其他goroutine进行累加操作,为了安全的获取计数值,我们通过LoadUint64函数获取计数器的一个拷贝,将计数器的内存地址给该函数
opsFinal := atomic.LoadUint64(&ops)
fmt.Println("ops: ", opsFinal)
}
1
2
tashuo:golang ta_shuo$ go run atomic-counter.go
ops: 41280

运行该程序,通过返回值可得知计数器累加了大概40000多次

原文链接:Go by Example: Atomic Counters

翻译自 https://gobyexample.com/

Go by Example

Go is an open source programming language designed for building simple, fast, and reliable software.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Go by Example: Rate Limiting

限速是控制资源利用率和维持服务质量的一种重要机制。golang通过goroutineschannelstickers优雅的支持限速。

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
package main

import "fmt"
import "time"

func main() {
// 首先来看下基本的限速功能
// 假设我们想限制对请求的处理
// 我们将从同一个管道中提供这样的请求
requests := make(chan int, 5)
for i := 1; i <= 5; i++ {
requests <- i
}
close(requests)

// 该管道每200毫秒接收一个值
// 这个是我们限速的核心
limiter := time.Tick(time.Millisecond * 200)

// 通过处理每个请求时管道接收值的阻塞,我们限制每200毫秒只处理一个请求
for req := range requests {
<-limiter
fmt.Println("request", req, time.Now())
}

// 某些时候可能希望在保持整体限速规则下允许少量的请求不受限制
// 可以通过含有缓冲区的管道来实现
// burstyLimiter管道可以使同时处理的请求达到3个
burstyLimiter := make(chan time.Time, 3)
for i := 0; i < 3; i++ {
burstyLimiter <- time.Now()
}

// 每200毫秒发送给burstyLimiter管道一个值
go func() {
for t := range time.Tick(time.Millisecond * 200) {
burstyLimiter <- t
}
}()

// 模拟5个新的请求,前三个请求由于burstyLimiter管道缓冲区的处理可以突破200毫秒的限制
burstyRequests := make(chan int, 5)
for i := 1; i <= 5; i++ {
burstyRequests <- i
}
close(burstyRequests)

for req := range burstyRequests {
<-burstyLimiter
fmt.Println("request", req, time.Now())
}
}
1
2
3
4
5
6
7
8
9
10
11
tashuo:golang ta_shuo$ go run rate-limiting.go
request 1 2017-11-24 15:45:04.641607273 +0800 CST
request 2 2017-11-24 15:45:04.838581623 +0800 CST
request 3 2017-11-24 15:45:05.040839234 +0800 CST
request 4 2017-11-24 15:45:05.241695212 +0800 CST
request 5 2017-11-24 15:45:05.441367122 +0800 CST
request 1 2017-11-24 15:45:05.441428032 +0800 CST
request 2 2017-11-24 15:45:05.441435722 +0800 CST
request 3 2017-11-24 15:45:05.441440602 +0800 CST
request 4 2017-11-24 15:45:05.641538132 +0800 CST
request 5 2017-11-24 15:45:05.84206538 +0800 CST

前5个请求跟预期一样是每200秒处理一个,后面5个请求中的前3个并没有被200毫秒的规则限制到。

原文链接:Go by Example: Rate Limiting

翻译自 https://gobyexample.com/

Go by Example

Go is an open source programming language designed for building simple, fast, and reliable software.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Go by Example: Worker Pools

这节探索如何使用goroutinechannel实现工作池的功能。

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
package main

import "fmt"
import "time"

// 这个是工作进程,用来执行并发的任务
// 每个进程都会从管道jobs接受工作,并且将相应发送至管道results
// 每个工作我们都会睡眠一秒用以模拟一项耗时的任务
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Println("worker", id, "started job", j)
time.Sleep(time.Second)
fmt.Println("worker", id, "finished job", j)
results <- j * 2
}
}

func main() {
// 初始化两个管道用以给工作进程分配任务及接收结果
jobs := make(chan int, 100)
results := make(chan int, 100)

// 启动三个工作进程
// 此时它们是阻塞的,因为管道jobs是空的
for w:= 1; w <= 3; w++ {
go worker(w, jobs, results)
}

// 初始化5个任务
// 关闭管道jobs,标识没有多余的任务了
for j:= 1; j <= 5; j++ {
jobs <- j
}
close(jobs)

// 收集所有任务的结果
for a := 1; a <= 5; a++ {
<-results
}
}
1
2
3
4
5
6
7
8
9
10
11
tashuo:golang ta_shuo$ go run worker-pool.go
worker 3 started job 1
worker 2 started job 3
worker 1 started job 2
worker 3 finished job 1
worker 3 started job 4
worker 2 finished job 3
worker 1 finished job 2
worker 2 started job 5
worker 2 finished job 5
worker 3 finished job 4

通过输出可以看出5个任务被多个工作进程执行。程序只消耗了2秒多而不是5秒,是因为所有的任务被3个工作进程并发处理。

原文链接:Go by Example: Worker Pools

翻译自 https://gobyexample.com/

Go by Example

Go is an open source programming language designed for building simple, fast, and reliable software.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Go by Example: Tickers

Timer是当你想要在未来某个时间只执行一次某个事件时使用,而ticker可以用来以一定时间间隔重复执行一个事件。

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 "time"
import "fmt"

func main() {
// Ticker跟Timer是类似的机制:使用管道来通信
// 下面示例中我们使用内置的for-range语句对于每500毫秒接收到一个的值进行迭代处理
ticker := time.NewTicker(time.Millisecond * 500)
go func() {
for t := range ticker.C {
fmt.Println("Tick at", t)
}
}()

// 跟Timer一样,Ticker也可以被停止
// 当Ticker取消时,其相应的管道不会再接收到值
// 该示例中我们在1600毫秒后停止
time.Sleep(time.Millisecond * 1600)
ticker.Stop()
fmt.Println("Ticker stoped")
}
1
2
3
4
5
tashuo:golang ta_shuo$ go run ticker.go
Tick at 2017-09-06 11:47:35.512980179 +0800 CST
Tick at 2017-09-06 11:47:36.012186259 +0800 CST
Tick at 2017-09-06 11:47:36.515210336 +0800 CST
Ticker stoped

在执行1600毫秒被停止时Ticker 500毫秒的间隔应该已经重复执行了三次

原文链接:Go by Example: Tickers

翻译自 https://gobyexample.com/

Go by Example

Go is an open source programming language designed for building simple, fast, and reliable software.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Go by Example: Timers

我们经常希望golang代码在未来某个时间点执行或者以一定的时间间隔重复执行,golang内置的timerticker功能可以轻松实现这两种工作。

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
package main

import "time"

func main() {
// Timer类似于未来一个独立的事件,可以告诉它你要等待多久,它会提供一个管道用来在时间点到时发出通知
// 该例中会等待2秒
timer1 := time.NewTimer(time.Second * 2)

// <- timer1.C会一直阻塞直到时间过期管道发送出一个值
<- timer1.C
println("Timer1 expired")

// 如果只是想等待一段时间,可以使用`time.Sleep`
// 但使用timer你可以在时间到期前取消,这个也是Sleep做不到的
timer2 := time.NewTimer(time.Second)
go func() {
<-timer2.C
println("Timer2 expired")
}()

// 使用Stop来取消timer
stop2 := timer2.Stop()
if stop2 {
println("Timer2 stopped")
}
}
1
2
3
tashuo:golang ta_shuo$ go run timer.go
Timer1 expired
Timer2 stopped

第一个timer会在2秒后过期,但是第二个timer在过期前被Stop,所以没有过期

原文链接:Go by Example: Timers

翻译自 https://gobyexample.com/

Go by Example

Go is an open source programming language designed for building simple, fast, and reliable software.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Go by Example: Range over Channels

在前面的示例中看到forrange语句如何给基本数据类型提供迭代操作,我们也可以用它们来迭代channel中接收到的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
// 定义有两个字符串缓存区的channel queue
// 我们将要迭代queue中的两个值
queue := make(chan string, 2)
queue <- "one"
queue <- "two"

close(queue)

// range会迭代queue中接收到的每个值
// 由于在上面已经close掉管道queue,所以在接收完两个值之和迭代就结束了
for elem := range queue {
fmt.Println(elem)
}
}
1
2
3
tashuo:golang ta_shuo$ go run r_channel.go
one
two

上面的示例中也看出可以关闭掉一个还有待接收值的非空管道。

原文链接:Go by Example: Range over Channels

翻译自 https://gobyexample.com/

Go by Example

Go is an open source programming language designed for building simple, fast, and reliable software.

Go by Example is a hands-on introduction to Go using annotated example programs. Check out the first example or browse the full list below.

Go by Example: Closing Channels

关闭一个管道表明已经没有值会通过它来发送,这个对于向管道的接收者表达已完成的信号是有用的。

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

func main() {
// 在这个示例中我们使用一个管道jobs来分发工作,这些工作从main goroutine发送到另一个工作goroutine中执行
// 如果没有剩余工作要做就关闭管道jobs
jobs := make(chan int, 5)
done := make(chan bool)

// 这个就是上文提到的工作goroutine
// 它通过‘j, more := <-jobs’重复取值,如果管道jobs被关闭并且所有值都已经接收,more的值将会是false
// 我们通过这种方式在已经做完所有工作时通知完成的状态
go func() {
for {
j, more := <-jobs
if more {
println("recieved job", j)
} else {
println("recieved all jobs")
done <- true
return
}
}
}()

// 通过管道jobs发送三个任务,然后关闭管道
for j := 1; j < 3; j++ {
jobs <- j
println("sent job", j)
}

close(jobs)
println("sent all jobs")

// 通过管道同步来等待工作goroutine
<-done
}
1
2
3
4
5
6
7
tashuo:golang ta_shuo$ go run closing_channel.go
sent job 1
sent job 2
sent all jobs
recieved job 1
recieved job 2
recieved all jobs

原文链接:Go by Example: Closing Channels