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主页上
的提示。