文章目录
Go 指南的Concurrency
这里主要总结Go指南中的提示:以下是我对官方指南的摘要
一、基本使用
Go routine
在func前面加go就会生成一个go routine
func RunGoroutine() {
go func() {
say("Hello, World")
}()
}
func say(s string) {
fmt.Println(s)
}
Channels
channels用于两个routine之间的通信的,这里利用两个channels同时计算数组元素的和
func sum(s []int, c chan int) {
sum := 0
for _, v := range s{
sum += v
}
c <- sum
}
func CalculateSum() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <- c, <- c
fmt.Println(x, y, x+y)
}
Buffered Channels
就是buffer满了才开始通信
原来如果没有buffer,如下的Code是不能运行的,因为一开始channel只有1的容量,当消息发出去的时候需要马上接,否则满了就不能在接受下一个传过来的值了
func Buffer() {
ch := make(chan int)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
加上buffer就可以运行了,因为有了数量为2个的缓冲
func Buffer() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
Range and Close
发送方如果需要关闭channel有2种方法
1. v, ok := <-ch
2. for i := range c
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
map的线程安全
在Go中的map并不是线程安全的,或者说在使用Goroutine的时候会出现 fatal error: concurrent map read and map write
解决方法是:
使用 sync.Mutex
type Counter struct {
v map[string] int
Lock sync.Mutex
}
func (c *Counter) Inc(key string) {
c.Lock.Lock()
defer c.Lock.Unlock()
c.v[key]++
}
func (c *Counter) Value(key string) int{
c.Lock.Lock()
defer c.Lock.Unlock()
return c.v[key]
}
func RunInc() {
c := Counter{v: map[string]int{}}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}
二、练习 Equivalent Binary Trees
题目如下:
Exercise: 寻找等价的BST
需要实现
Walk 将所有的值从 tree 发送到 channel ch。
Same 检测树 t1 和 t2 是否含有相同的值。
我的实现如下:
- Walk 主要是中序遍历,把值传给channel
- Same 主要是处理传过来的值,最对比
func Walk(t *tree.Tree, ch chan int) {
if t == nil {
return
}
Walk(t.Left, ch)
ch <- t.Value
Walk(t.Right, ch)
}
func Same(t1, t2 *tree.Tree) bool {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
Walk(t1, ch1)
close(ch1)
}()
go func() {
Walk(t2, ch2)
close(ch2)
}()
for {
x, ok := <-ch1
if !ok {
break
}
y, ok := <-ch2
if !ok {
break
}
if x != y {
return false
}
fmt.Println("x and y are ", x, y)
}
return true
}
三、练习web crawler
我的版本, 主要是通过map做数据的存储,通过Mutex保证数据的一直性
type UrlMap struct {
m map[string]int
mux sync.Mutex
}
func (urlMap *UrlMap) getUrl(url string) int {
urlMap.mux.Lock()
defer urlMap.mux.Unlock()
return urlMap.m[url]
}
func (urlMap *UrlMap) setUrl(url string) {
urlMap.mux.Lock()
defer urlMap.mux.Unlock()
urlMap.m[url]++
}
// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher, urlMap *UrlMap) {
if depth <= 0 {
return
}
if urlMap.getUrl(url) > 1 {
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
return
}
var newUrls []string
for _, u := range urls {
if urlMap.getUrl(u) == 0 {
urlMap.setUrl(u)
newUrls = append(newUrls, u)
}
}
fmt.Printf("found: %s %q\n", urls, body)
for _, u := range newUrls {
go Crawl(u, depth-1, fetcher, urlMap)
}
return
}
func RunCrawl() {
urlMap := UrlMap{m: map[string]int{"https://golang.org/": 1}}
Crawl("https://golang.org/", 4, fetcher, &urlMap)
time.Sleep(100 * time.Millisecond)
}
总结
Goroutine的线程需要有好的设计才能更好地发挥起作用,这里的知识还是很多