Go语言学习

4.7 出让时间片

我们可以在每个goroutine中控制何时主动出让时间片给其他goroutine,这可以使用runtime

包中的Gosched()函数实现。

实际上,如果要比较精细地控制goroutine的行为,就必须比较深入地了解Go语言开发包中

runtime包所提供的具体功能。

4.8.1 同步锁

Go语言包中的sync包提供了两种锁类型:sync.Mutex和sync.RWMutex。Mutex是最简单

的一种锁类型,同时也比较暴力,当一个goroutine获得了Mutex后,其他goroutine就只能乖乖等

到这个goroutine释放该Mutex。RWMutex相对友好些,是经典的单写多读模型。在读锁占用的情

况下,会阻止写,但不阻止读,也就是多个goroutine可同时获取读锁(调用RLock()方法;而写

锁(调用Lock()方法)会阻止任何其他goroutine(无论读和写)进来,整个锁相当于由该goroutine

独占。

4.8.2 全局唯一性操作

对于从全局的角度只需要运行一次的代码,比如全局初始化操作,Go语言提供了一个Once

类型来保证全局的唯一性操作,具体代码如下:

var a string

var once sync.Once

func setup() {

a = "hello, world"

}

func doprint() {

once.Do(setup)

print(a)

}

func twoprint() {

go doprint()

go doprint()

}

如果这段代码没有引入Once,setup()将会被每一个goroutine先调用一次,这至少对于这个

例子是多余的。在现实中,我们也经常会遇到这样的情况。Go语言标准库为我们引入了Once类

型以解决这个问题。once的Do()方法可以保证在全局范围内只调用指定的函数一次(这里指

setup()函数),而且所有其他goroutine在调用到此语句时,将会先被阻塞,直至全局唯一的

once.Do()调用结束后才继续。

tcp网络编程

package main

import (

  "net"

  "os"

  "bytes"

  "fmt"

)

func main() {

  if len(os.Args) != 2 {

    fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0])

    os.Exit(1)

  }

  service := os.Args[1]

  conn, err := net.Dial("tcp", service)

  checkError(err)

  _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))

  checkError(err)

  result, err := readFully(conn)

  checkError(err)

  fmt.Println(string(result))

  os.Exit(0)

}

func main() {

  if len(os.Args) != 2 {

    fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0])

    os.Exit(1)

  }

  service := os.Args[1]

  tcpAddr, err := net.ResolveTCPAddr("tcp4", service)

  checkError(err)

  conn, err := net.DialTCP("tcp", nil, tcpAddr)

  checkError(err)

  _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))

  checkError(err)

  result, err := ioutil.ReadAll(conn)

  checkError(err)

  fmt.Println(string(result))

  os.Exit(0)

}

func checkError(err error) {

if err != nil {

fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())

os.Exit(1)

}

}

与之前使用Dail()的例子相比,这里有两个不同:

 net.ResolveTCPAddr(),用于解析地址和端口号;

 net.DialTCP(),用于建立链接。

这两个函数在Dial()中都得到了封装。

8.3 Vim配置

Go语言安装包中已经包含了对Vim的环境支持。要将Vim配置为适合作为Go语言的开发环

境,我们只需要按$GOROOT/misc/vim中的说明文档做以下设置即可。

创建一个shell脚本govim.sh,该脚本的内容如下:

mkdir -p $HOME/.vim/ftdetect

mkdir -p $HOME/.vim/syntax

mkdir -p $HOME/.vim/autoload/go

ln -s $GOROOT/misc/vim/ftdetect/gofiletype.vim $HOME/.vim/ftdetect/

ln -s $GOROOT/misc/vim/syntax/go.vim $HOME/.vim/syntax

ln -s $GOROOT/misc/vim/autoload/go/complete.vim $HOME/.vim/autoload/go

echo "syntax on" >> $HOME/.vimrc

在执行该脚本之前,先确认GOROOT环境变量是否正确设置并已经起作用,具体代码如下:

$ echo $GOROOT

/usr/local/go

如果上面这个命令的输出为空,则表示GOROOT尚未正确设置,请保证GOROOT环境变量正确

设置后再执行上面的govim.sh脚本。

现在可以执行这个脚本了,该脚本只需要执行一次。执行成功的话,在$HOME目录下将会

创建一个.vim目录。之后再用Vim打开一个go文件,读者应该就可以看到针对Go语言的语法高亮

效果了。

Vim还可以配合gocode支持输入提示功能。接下来我们简单地配置一下。

首先获取gocode:

$ go get -u github.com/nsf/gocode

这个命令会下载gocode相应内容到Go的安装目录去(比如/usr/local/go),因此需要保证有目录的

写权限。然后开始配置gocode:

$ cd /usr/local/go/src/pkg/github.com/nsf/gocode/

$ cd vim

$ ./update.bash

配置就是这么简单。现在使用以下Vim的语法提示效果。用Vim创建一个新的Go文件(比如

命名为auto.go),输入以下内容:

package main

import "fmt"

func main() {

fmt.Print

请将光标停在fmt.Print后面,然后按组合键Ctrl+X+O(三个键同时按住后放开),你会看

到fmt包里的所有3个以Print开头的全局函数都被列了出来:Print、Printf和Println。之

后就可以用上下方向键选取,按回车键即可完成输入,非常方便。

gocode其实是一个独立地提供输入提示的服务器程序,并非专为Vim打造。比如Emacs也

可以很容易地添加基于gocode的Go语言输入提示功能。大家可以查看gocode的Github主页上

的提示。