MapReduce的变体:HashFold介绍

说到MapReduce计算模型大家应该都清楚。今天我们来看一下MapReduce一个变体:HasFold。

閱讀更多

防御性编程小例

今天面试一个人时,突然想起一个话题:防御性编程 。我们先看一个小程序,这也是以前出过的一道简单面试题:写一个函数,可以将一个小写字母转换为大写字母。这道简单的题,完全写对的答案却少之又少。大部分答案是这样的:

閱讀更多

对Go的Slice进行Append的一个坑

今天我们说说Go为数不多的一个“坑”。这个“坑”的代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
arr1 := [5]int{1, 2, 3, 4, 5}
slice1 := arr1[1:2]
slice1 = append(slice1, 6, 7, 8)
fmt.Println("slice1:", slice1)
fmt.Println("arr1:", arr1)

arr2 := [5]int{1, 2, 3, 4, 5}
slice2 := arr2[1:3]
slice2 = append(slice2, 6, 7, 8)
fmt.Println("slice2:", slice2)
fmt.Println("arr2:", arr2)
}

閱讀更多

Golang的Slice机制解析

Rob Pike写了篇关于Go的数组与切片的文章:Arrays, slices (and strings): The mechanics of ‘append’ ,介绍了slice的实现和一些常见的操作。其部分内容与我这篇文章是重复的,所以就不一一翻译了,而是挑选部分内容记下,算是对我这篇文章 的一个内容补充。
对于以下内容的理解,首先需要理解这篇文章提到的关于slice的结构定义,即可以用一个包含长度和一个指向数组的指针(当然还有容量)的struct来描述。

閱讀更多

Linux文件系统十问

今天读到这篇文章:Linux文件系统十问,你知道吗?,作了个总结笔记:

閱讀更多

怎么写Go的基准测试

Dave Cheney在他的blog写了一篇关于Go的基准测试编写的基本介绍(链接)。我以此为内容,整理输出内容。

閱讀更多

春秋五霸之首——齐桓公的故事

昨晚重新翻阅了《史记·齐太公世家》,今天就跟大家重新说说春秋五霸之首齐桓公姜小白的故事吧。

閱讀更多

Go并发编程模式进阶

前段时间Google的Sameer Ajmani在Google I/O上做了关于Go的并发模式的介绍。Slides在此,youtube视频在此(注:上述链接均需翻墙)。

本篇的前提是对goroutine+channel的并发编程模式有基本的了解,建议能读懂下面这个经典ping-pong程序为好。

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

import (
"fmt"
"time"
)
//定义一个结构
type Ball struct{ hits int }

func main() {
//创建一个可传输Ball的channel
table := make(chan *Ball)
//分别启动ping/pong的goroutine
go Player("Ping", table)
go Player("Pong", table)
//一个Ball进入channel,游戏开始
table <- new(Ball)
//“主”程序暂停1s,等待ping/pong的goroutine执行
time.Sleep(1 * time.Second)
//从channel取出Ball,游戏开始
<-table
//可通过引发异常,显示调用栈的详细信息
//panic("show me the stacks")
}

func Player(name string, table chan *Ball) {
for {
//channel取出Ball,并hits++
ball := <-table
ball.hits++
fmt.Println(name, ball.hits)
//暂停1ms
time.Sleep(1 * time.Millisecond)
//将Ball放回channel
table <- ball
}
}

ping-pong程序的执行过程,可以用下图来表示。

ping-pong程序执行过程

接下来主要说说Go的并发编程里的一些“文艺”使用:如何通信?如何周期性处理事件?如何取消执行?这些高级用法的支持,除了依赖我们上面看到的goroutine+channel外,还要依赖于Go的一个statement:select+case。它可以用来管理和监听多个channel,从而起到“多路复用”的效果。他的基本语法如下。

1
2
3
4
5
6
select {
case xc <- x:
// 向channel(xc)发送一个对象(x)
case y := <-yc:
// 从channel(yc)获取一个对象并赋值到变量(y)
}

下面我们以一个能持续从RSS获取资源项的例子来说明select的使用。
假设我们已经拥有下面这个接口所定义的功能:从一个RSS url获取资源项目(一次调用,获取一次,这个接口的模拟实现,见附1。)

1
2
3
type Fetcher interface {
Fetch() (items []Item, next time.Time, err error)//能从某个rss url获取它的资源项,并能同时返回下一次获取的时间next
}

`我们用下面这个接口来表示我们希望达到的功能:能从rss url上循环获取资源项,形成资源流的形式;循环获取功能,可以中止。

1
2
3
4
type Subscription interface {
Updates() <-chan Item//用channel来存放资源,即可实现流的显示
Close() error//关闭获取
}

先看一个这项功能的简单实现,熟悉多线程编程的,应该觉得很眼熟。

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

type NavieSub struct {
closed bool
err error
updates chan Item
fetcher Fetcher
}

func (s *NavieSub) Close() error {
s.closed = true//设置关闭标识为true
return s.err
}
func (s *NavieSub) Updates() <-chan Item {
return s.updates//返回已经获取的资源项
}
func (s *NavieSub) loop() {//循环获取的方法实现
for {
if s.closed {//判断关闭标识
close(s.updates)//close是内置函数
return
}
items, next, err := s.fetcher.Fetch()//执行一次获取
if err != nil {
s.err = err
time.Sleep(10 * time.Second)
continue//出错时暂停10秒后再开始下次循环
}
for _, item := range items {//将获取的资源项写入,用于返回
s.updates <- item
}
if now := time.Now(); next.After(now) {//暂停到下次获取时间时,再开始下一次获取
time.Sleep(next.Sub(now))
}
}
}

func main() {
fetcher := &FakeFether{channel: "sharecore.info"}
s := &NavieSub{
fetcher: fetcher,
updates: make(chan Item),
}

go s.loop()//启动一个例程执行loop方法(与启动一个线程类似)

time.AfterFunc(3*time.Second, func() {
fmt.Println("closed", s.Close())
})

for item := range s.Updates() {
fmt.Println(item.Channel, item.Title)
}
}

`那以上的简单实现,会有什么问题呢?

首先,明显发现s.err和s.closed的访问是非同步的。

1
2
3
4
5
6
s.closed = true //设置关闭标识为true

if s.closed {//判断关闭标识
close(s.updates) //close是内置函数
return
}

`然后,我们看到s.updates的定义如下:

1
2
3
4
s := &NavieSub{
fetcher: fetcher,
updates: make(chan Item),//定义为没有buffer的channel,一个channel中同时只能有一个元素
}

`根据上面的定义,s.updates一次只能有一个item进入,当它没有其他goroutine从它里面取出元素时,下面这行代码会发生堵塞

1
s.updates <- item

`那以上问题我们有什么办法来避免呢?

閱讀更多

Linux Container的安装与使用介绍

今天想在机器上搭建一个我用Go写的玩的一个分布式文件小系统,可我只有一台Laptop和一台Desktop,两台都装的Ubuntu 12.04。我需要多几个隔离的OS来试验多节点。以前阅读看到过Linux Container的介绍,于是研究了下,用它来搭建了虚拟环境,在此作个记录,方便大家:
首先,安装 Linux Container:

1
$ sudo apt-get install lxc

閱讀更多