《Go开发指南》翻译邀请
7月并发网组织翻译GO官网教程https://golang.google.cn/doc/相关技术文章,欢迎大家踊跃参加。
如何领取
通过评论领取想要翻译的文章,每次领取一章或一节(根据内容长短),翻译完后再领取其他章节。领取完成之后,译文最好在一个星期内翻译完成,如果不能完成翻译,也欢迎你邀请其他同学和你一起完成翻译。请谨慎领取,很多文章领取了没有翻译,导致文章很长时间没人翻译。
阅读全文7月并发网组织翻译GO官网教程https://golang.google.cn/doc/相关技术文章,欢迎大家踊跃参加。
通过评论领取想要翻译的文章,每次领取一章或一节(根据内容长短),翻译完后再领取其他章节。领取完成之后,译文最好在一个星期内翻译完成,如果不能完成翻译,也欢迎你邀请其他同学和你一起完成翻译。请谨慎领取,很多文章领取了没有翻译,导致文章很长时间没人翻译。
阅读全文声明:本文是《Go并发编程实战》的样章,感谢图灵授权并发编程网站发布样章,禁止以任何形式转载此文。
我们在本章前面的部分中对Go语言提供的各种传统同步工具和方法进行了逐一的介绍。在本节,我们将运用它们来构造一个并发安全的字典(Map)类型。
我们已经知道,Go语言提供的字典类型并不是并发安全的。因此,我们需要使用一些同步方法对它进行扩展。这看起来并不困难。我们只要使用读写锁将针对一个字典类型值的读操作和写操作保护起来就可以了。确实,读写锁应该是我们首先想到的同步工具。不过,我们还不能确定只使用它是否就足够了。不管怎样,让我们先来编写并发安全的字典类型的第一个版本。
我们先来确定并发安全的字典类型的行为。还记得吗?依然,这需要声明一个接口类型。我们在第4章带领读者编写过OrderedMap接口类型及其实现类型。我们可以借鉴OrderedMap接口类型的声明并编写出需要在这里声明的接口类型ConcurrentMap。实际上,ConcurrentMap接口类型的方法集合应该是OrderedMap接口类型的方法集合的一个子集。我们只需从OrderedMap中去除那些代表有序Map特有行为的方法声明即可。既然是这样,我何不从这两个自定义的字典接口类型中抽出一个公共接口呢?
声明:本文是《Go并发编程实战》的样章,感谢图灵授权并发编程网站发布样章,禁止以任何形式转载此文。
本章要讲解的是sync.Pool类型。我们可以把sync.Pool类型值看作是存放可被重复使用的值的容器。此类容器是自动伸缩的、高效的,同时也是并发安全的。为了描述方便,我们也会把sync.Pool类型的值称为临时对象池,而把存于其中的值称为对象值。至于为什么要加“临时“这两个字,我们稍后再解释。
我们在用复合字面量初始化一个临时对象池的时候可以为它唯一的公开字段New赋值。该字段的类型是func() interface{},即一个函数类型。可以猜到,被赋给字段New的函数会被临时对象池用来创建对象值。不过,实际上,该函数几乎仅在池中无可用对象值的时候才会被调用。
类型sync.Pool有两个公开的方法。一个是Get,另一个是Put。前者的功能是从池中获取一个interface{}类型的值,而后者的作用则是把一个interface{}类型的值放置于池中。
声明:本文是《Go并发编程实战》的样章,感谢图灵授权并发编程网站发布样章,禁止以任何形式转载此文。
我们在第6章多次提到过sync.WaitGroup类型和它的方法。sync.WaitGroup类型的值也是开箱即用的。例如,在声明
[code lang=”java”]
var wg sync.WaitGroup
[/code]
之后,我们就可以直接正常使用wg变量了。该类型有三个指针方法,即Add、Done和Wait。
类型sync.WaitGroup是一个结构体类型。在它之中有一个代表计数的字段。当一个sync.WaitGroup类型的变量被声明之后,其值中的那个计数值将会是0。我们可以通过该值的Add方法增大或减少其中的计数值。例如:
声明:本文是《Go并发编程实战》的样章,感谢图灵授权并发编程网站发布样章,禁止以任何形式转载此文。
现在,让我们再次聚焦到sync代码包。除了我们介绍过的互斥锁、读写锁和条件变量,该代码包还为我们提供了几个非常有用的API。其中一个比较有特色的就是结构体类型sync.Once和它的Do方法。
与代表锁的结构体类型sync.Mutex和sync.RWMutex一样,sync.Once也是开箱即用的。换句话说,我们仅需对它进行简单的声明即可使用,就像这样:
[code lang=”java”]
var once sync.Once
once.Do(func() { fmt.Println("Once!") })
[/code]
声明:本文是《Go并发编程实战》的样章,感谢图灵授权并发编程网站发布样章,禁止以任何形式转载此文。
我们已经知道,原子操作即是进行过程中不能被中断的操作。也就是说,针对某个值的原子操作在被进行的过程当中,CPU绝不会再去进行其它的针对该值的操作。无论这些其它的操作是否为原子操作都会是这样。为了实现这样的严谨性,原子操作仅会由一个独立的CPU指令代表和完成。只有这样才能够在并发环境下保证原子操作的绝对安全。
Go语言提供的原子操作都是非侵入式的。它们由标准库代码包sync/atomic中的众多函数代表。我们可以通过调用这些函数对几种简单的类型的值进行原子操作。这些类型包括int32、int64、uint32、uint64、uintptr和unsafe.Pointer类型,共6个。这些函数提供的原子操作共有5种,即:增或减、比较并交换、载入、存储和交换。它们分别提供了不同的功能,且适用的场景也有所区别。下面,我们就根据这些种类对Go语言提供的原子操作进行逐一的讲解。
声明:本文是《Go并发编程实战》的样章,感谢图灵授权并发编程网站发布样章,禁止以任何形式转载此文。
我们在第6章讲多线程编程的时候详细说明过条件变量的概念、原理和适用场景。因此,我们在本小节仅对sync代码包中与条件变量相关的API进行简单的介绍,并使用它们来改造我们之前实现的*myDataFile类型的相关方法。
在Go语言中,sync.Cond类型代表了条件变量。与互斥锁和读写锁不同,简单的声明无法创建出一个可用的条件变量。为了得到这样一个条件变量,我们需要用到sync.NewCond函数。该函数的声明如下:
[code lang=”java”]
func NewCond(l Locker) *Cond
[/code]