Go语言之初识Goroutine
1. Go的并发机制
面对前来面试Java程序员的求职者的时候我一般都会问问并发编程,毕竟并发编程总是属于Java编程的部分。其实Java的并发编程已经被设计的很简单了,不过要想掌握并发编程也并不是一件很轻松的事情。
作为一个DBA,面对高并发的场景算是司空见惯的了,没有并发能力的数据库(对,我说的就是MyISAM),还不如玩具。
那么数据库并发的时候重要的几个点是什么呢?
- 锁,分为排他锁和共享锁,我认为这是个天才的设计;
- 连接池,使用了连接池可以大大降低高并发时的负载;
- 索引,索引能大幅提升查询效率,降低锁持有的时间。
其实说了这三样,还是锁重要,锁保护了并发时出现竞态时的资源安全。实际上在Java编程的时候也总是说什么线程安全之类的概念,无非也就是对竞争状态下资源的保护。
Go作为一个现代的语言,其并发模型也是很简单的,这一点确实比Python好,很多人认为Python是没有并发编程的,这也不能算是全错,至少Python的并发能力和Java比起来还是差不少。
扯了这么多闲话,画一张图来说明一下Go的并发:
- G4到G7代表着goroutine,一旦创建出来就会被分配给一个逻辑处理器,这里记为P0;
- 逻辑处理器P0被绑定到一个系统线程上;
- 逻辑处理器是可以在代码里分配的。
这个模型看起来是还是怪怪的,因为我们知道计算机里有个很重要的概念叫做中断,大致意思是CPU每个时间片只能处理一件事,但是CPU可以分配时间片,即处理一会儿任务A,然后中断,分配一些时间片给任务B,因为切换时间太快,反应迟钝的人是反应不过来的,还以为计算机是并行处理的。
那么逻辑处理器要如何进行并发呢,这看起来还是不像并发的样子。事实上Go提供了这样的能力,不然还谈什么并发编程。
Go遇到需要阻塞的goroutine,就会分配一个新的线程,将这个goroutine和原来的线程分离,直到该阻塞线程有返回。这段时间里,逻辑处理器P0继续做队列里其他的事情。
2. 编码实现
下面来写一段代码展示如何实现并发:
package main
import (
"fmt"
"runtime"
"sync"
)
var wg sync.WaitGroup
func main() {
runtime.GOMAXPROCS(1)
wg.Add(2)
fmt.Println("Start........")
go printPrime("A")
go printPrime("B")
fmt.Println("Waiting to finish")
wg.Wait()
fmt.Println("Done!")
}
func printPrime(prefix string) {
defer wg.Done()
next:
for outer := 2; outer < 50000; outer++ {
for inner := 2; inner < outer; inner++ {
if outer % inner == {
continue next
}
}
fmt.Printf("%s: %d\n", prefix, outer)
}
fmt.Printf("Finish %s\n", prefix)
}
相关文章