不会飞的章鱼

熟能生巧,勤能补拙,静能生慧;念念不忘,必有回响。

sync包还是channel

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
var cs = 0 // 模拟临界区要保护的数据
var mu sync.Mutex
var c = make(chan struct{}, 1)

func criticalSectionSyncByMutex() {
mu.Lock()
cs++
mu.Unlock()
}

func criticalSectionSyncByChan() {
c <- struct{}{}
cs++
<-c
}

func BenchmarkCriticalSectionSyncByMutex(b *testing.B) {
for n := 0; n < b.N; n++ {
criticalSectionSyncByMutex()
}
}

func BenchmarkCriticalSectionSyncByChan(b *testing.B) {
for n := 0; n < b.N; n++ {
criticalSectionSyncByChan()
}
}

/*
BenchmarkCriticalSectionSyncByMutex-8 76766581 15.41 ns/op
BenchmarkCriticalSectionSyncByChan-8 32243965 37.59 ns/op
*/

使用sync包的注意事项

sync包源文件中,我们看到以下注释:

1
2
3
4
5
// A Mutex must not be copied after first use.

// A RWMutex must not be copied after first use.

// A Cond must not be copied after first use.
阅读全文 »

defer的运作机制

  • 在 Go 中,只有在函数(和方法)内部才能使用 defer;
  • defer 关键字后面只能接函数(或方法),这些函数被称为deferred 函数。defer 将它们注册到其所在 Goroutine 中,用于存放 deferred 函数的栈数据结构中,这些deferred 函数将在执行 defer 的函数退出前,按后进先出(LIFO)的顺序被程序调度执行:

defer的常见用法

拦截panic

按需要对panic进行处理,可以尝试从panic中恢复。

阅读全文 »

Go语言的字符串类型

统一设置为string

1
2
3
4
5
6
7
8
9
10
const (
s = "string constant"
)
func main() {
var s1 string = "string variable"

fmt.Printf("%T\n", s) // string
fmt.Printf("%T\n", s1) // string
fmt.Printf("%T\n", "temporary string literal") // string
}

功能特点:

  • string类型的数据是不可变的;
  • 零值可用;
  • 获取长度的时间复杂度是O(1)级别
  • 支持通过 +/+= 操作符进行字符串连接
  • 支持各种比较关系操作符:==、!=、>=、<=、<、>
  • 对非ASCII字符提供原生支持
  • 原生支持多行字符串
阅读全文 »

什么是数组

数组有哪些基本特性

Go 语言的数组是一个长度固定的、由同构类型元素组成的连续序列。
因此Go 的数组类型包含两个重要属性:元素的类型和数组长度(元素的个数)。
所以,Go 语言中数组类型变量的声明:

1
var arr [N]T //声明了一个数组变量 arr,它的类型为[N]T,其中元素的类型为 T,数组的长度为N。

通过声明,我们可以得出一个结论:如果两个数组类型的元素类型 T 与数组长度 N 都是一样的,那么这两个数组类型是等价的,如果有一个属性不同,它们就是两个不同的数组类型。

阅读全文 »

什么是channel

channel是Go语言提供的一种重要的并发原语。它在Go语言的CSP模型中扮演者重要的角色:

  • 可以实现goroutine之间的通信;
  • 可以实现goroutine之间的同步。

channel原语的使用:

1
2
3
4
5
6
7
8
9
10
11
12
c := make(chan int)  // 创建一个无缓冲的int类型的channel
c := make(chan int,5) // 创建一个带缓冲的int类型的channel
c <- x // 向channel c 中发送一个值
<- c // 从channel c 中接收一个值
x = <- c // 从channel c 接收一个值并将其存储到变量x中
x,ok = <- c // 从channel c 接收一个值。若channel关闭了,ok将被置为false
for i := range c {...}
close(c) // 关闭channel c

c := make(chan chan int) // 创建一个无缓冲的chan int类型的channel
func stream(ctx context.Context, out chan<- Value) error // 将只发送channel作为函数参数
func spwn(...) <-chan T // 将只接收类型channel作为返回值
阅读全文 »

什么是goroutine

goroutine是由Go运行时管理的用户层轻量级线程。

相比较于操作系统线程,goroutine的资源占用和使用代价都要小得多,可以创建几十个、几百个甚至成千上万个goroutine也不会造成系统资源的枯竭,Go的运行时负责对goroutine进行管理。而所谓的管理 == 调度。

goroutine调度器

一个Go程序中可以创建成千上万个并发的goroutine,而将这些goroutine按照一定算法放到CPU上执行的程序就称为goroutine调度器

阅读全文 »