协程和管道
1、协程
1.1、进程、线程和协程
进程(Process)就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位,进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间。一个进程至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。
线程是进程的一个执行实例,是程序执行的最小单元,它是比进程更小的能独立运行的基本单位
并发:多个线程同时竞争一个位置,竞争到的才可以执行,每一个时间段只有一个线程在执行。
并行:多个线程可以同时执行,每一个时间段,可以有多个线程同时执行。
通俗的讲多线程程序在单核CPU上面运行就是并发,多线程程序在多核CUP上运行就是并行,如果线程数大于CPU核数,则多线程程序在多个CPU上面运行既有并行又有并发。
Golang中的协程:
Golang中的主线程:(可以理解为线程/也可以理解为进程),在一个Golang程序的主线程上可以起多个协程。Golang 中多协程可以实现并行或者并发。
协程:可以理解为用户级线程,这是对内核透明的,也就是系统并不知道有协程的存在,是完全由用户自己的程序进行调度的。Golang的一大特色就是从语言层面原生支持协程,在函数或者方法前面加go关键字就可创建一个协程。可以说Golang中的协程就是goroutine 。
Golang 中的多协程有点类似其他语言中的多线程。
多协程和多线程:Golang 中每个goroutine (协程) 默认占用内存远比Java 、C的线程少。
OS线程(操作系统线程)一般都有固定的栈内存(通常为 2MB 左右),一个goroutine (协程)占用内存非常小,只有 2KB 左右,多协程 goroutine切换调度开销方面远比线程要少。
1.2、goroutine的使用以及sync.WaitGroup
下面实现创建一个协程,在协程和主线程中分别执行打印语句,每次休眠一秒。
package main
import (
"fmt"
"time"
)
func test() {
for i := 0; i < 3; i++ {
fmt.Println("test...")
time.Sleep(time.Second)
}
}
func main() {
go test()
for i := 0; i < 3; i++ {
fmt.Println("main...")
time.Sleep(time.Second)
}
}

但是有个问题,如果主线程执行的速度比较快呢,我们可以修改一下代码,让主线程跑快一些。

此时我们发现主线程执行完后,协程不会再继续执行了。这是因为主线程执行完后整个程序就退出了。
所以我们需要使用sync.WaitGroup来让主线程等待协程。
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func test() {
for i := 0; i < 3; i++ {
fmt.Println("test...")
time.Sleep(time.Second)
}
wg.Done()
}
func main() {
wg.Add(1)
go test()
for i := 0; i < 3; i++ {
fmt.Println("main...")
time.Sleep(100)
}
wg.Wait()
}

可以看到此时主线程执行完后会等待协程执行完,然后才会退出。有点类似于创建进程/线程并进行进程/线程等待回收。
其中:sync.WaitGroup本质上是一个计数器,Add方法表示增加计数器,Done表示让计数器减1,Wait表示等待协程执行完毕。
1.3、启动多个协程
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func test(id int) {
for i := 1; i <= 3; i++ {
fmt.Printf("我是协程[%v]..., i=%d\n", id, i)
}
wg.Done()
}
func main() {
for i := 1; i <= 5; i++ {
wg.Add(1)
go test(i)
}
wg.Wait()
}

1.4、设置Golang并行运行的时候占用的cup数量
可以使用runtime.NumCPU()来获取当前计算机上CPU核心的数量。
Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个 OS 线程来同时执行Go代码。默认值是机器上的 CPU 核心数。 例如在一个 8 核心的机器上,调度器会把 Go 代码同时调度到8个OS线程上。
Go语言中可以通过runtime.GOMAXPROCS()函数设置当前程序并发时占用的CPU逻辑核心数。
package main
import (
"fmt"
"runtime"
)
func main() {
num := runtime.NumCPU()
fmt.Println("CPU数量为:", num)
runtime.GOMAXPROCS(num - 1)
}
1.5、goroutine统计素数
现在假设要统计1->50000000中有多少素数,最普遍的做法是使用一个for循环来做,如下:
package main
import (
"fmt"
"time"
)
func main() {
u1 := time.Now().Unix()
// var cnt = 0
for i := 2; i <= 50000000; i++ {
flag := true
for j := 2; j*j <= i; j++ {
if i%j == 0 {
flag = false
break
}
}
if flag {
// cnt++
}
}
// fmt.Println("共有素数:", cnt)
u2 := time.Now().Unix()
fmt.Println("花费时间:", u2-u1)
}

我们发现运行时间高达12S。下面我们使用goroutine试试看:
我们创建5个协程来完成,每个协程处理一千万个数据。
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func test(x int) {
for i := (x-1)*10000000 + 1; i <= x*10000000; i++ {
if i == 1 {
continue
}
flag := true
for j := 2; j*j <= i; j++ {
if i%j == 0 {
flag = false
break
}
}
if flag {
}
}
wg.Done()
}
func main() {
u1 := time.Now().Unix()
for i := 1; i <= 5; i++ {
wg.Add(1)
go test(i)
}
wg.Wait()
u2 := time.Now().Unix()
fmt.Println("花费时间:", u2-u1)</

最低0.47元/天 解锁文章
1809

被折叠的 条评论
为什么被折叠?



