协程(gorouting)介绍以及sync.WaitGroup,素数
进程、线程
- 进程(Proces)就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基
本单位,进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进 程都有一个自己的地址空间。一个进程至少有 5种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。通俗的讲进程就是一个正在执行的程序。 - 线程 是进程的一个执行实例,是程序执行的最小单元,它是比进程更小的能独立运行的基 本单位
- 一个进程可以创建多个线程,同一个进程中的多个线程可以并发执行,一个程序要运行的话 至少有一个进程
并行和并发
- 并发:多个线程同时竞争一个位置,竞争到的才可以执行,每一个时间段只有一个线程在执 行
- 并行:多个线程可以同时执行,每一个时间段,可以有多个线程同时执行。
- 通俗的讲多线程序在单核 CPU 上面运行就是并发,多线程序在多核 CUP 上运行就是并 行,如果线程数大于 CPU核数,则多线程序在多个 CPU 上面运行既有并行又有并发
Golang 中的协程(goroutine)以及主线程
- golang 中的主线程:(可以理解为线程/也可以理解为进程),在一个 Golang 程序的主线程 上可以起多个协程.Golang中多协程可以实现并行或者并发
- 协程:可以理解为用户级线程,这是对内核透明的,也就是系统并不知道有协程的存在,是 完全由用户自己的程序进行调度的
- Golang 的一大特色就是从语言层面原生支持协程,在函数或者方法前面加 go 关键字就可创建一个协程。可以说 Golang中的协程就是goroutine
import (
"fmt"
"testing"
"time"
)
func otherFun() {
fmt.Println("开启一个协程")
time.Sleep(time.Millisecond * 100) //毫秒
}
func TestGorouting(t *testing.T) {
go otherFun() //表示开启一个协程
for i := 0; i < 10; i++ {
fmt.Println("main()_", i)
time.Sleep(time.Millisecond * 100)
}
}
- Golang 中的多协程有点类似其他语言中的多线程
- 多协程和多线程:Golang 中每个 goroutine (协程) 默认占用内存远比 Java 、C 的线程少,OS线程(操作系统线程)一般都有固定的栈内存(通常为 2MB 左右),一个 goroutine (协程) 占用内存非常小,只有 2KB左右,多协程 gorutine 切换调度开销方面远比线程要少这也是为什么越来越多的大公司使用 Golang 的原因之一
Gorutine 的使用以及 sync.WaitGroup
func otherFun() {
for i := 0; i < 10; i++ {
fmt.Printf("other()_%v\n",i)
time.Sleep(time.Millisecond * 100) //毫秒
}
}
func TestGorouting(t *testing.T) {
go otherFun() //表示开启一个协程
for i := 0; i < 10; i++ {
fmt.Println("main()_", i)
time.Sleep(time.Millisecond * 50) //毫秒
}
// 主线程执行完毕后,即使协程没有执行完毕程序也会退出
//一种方法 time.Sleep(time.Second*1)//等待1s 退出
}
sync.WaitGroup 可以实现主线程等待协程执行完毕
var wg sync.WaitGroup
func otherFun(n int) {
defer wg.Done() //协程计数-1
for i := 0; i < 10; i++ {
fmt.Printf("other()_第%v个协程值%v\n", n, i)
time.Sleep(time.Millisecond * 100) //毫秒
}
}
// 实现并发和并行
func TestGorouting(t *testing.T) {
for i := 0; i < 10; i++ {
wg.Add(1)//协程计数+1
go otherFun(i) //表示开启一个协程
}
for i := 0; i < 10; i++ {
fmt.Println("main()_", i)
time.Sleep(time.Millisecond * 50)
}
wg.Wait()//等待协程执行完成
}
多次执行上面的代码,会发现每次打印的数字的顺序都不一致。这是因为 10 个 goroutine
是并发执行的,而 goroutine 的调度是随机的。
设置 Golang 并行运行的时候占用的 cup 数量
- Go 运行时的调度器使用 GOMAXPROCS 参数来确定需要使用多少个 OS 线程来同时执行 Go 代码。默认值是机器上的 CPU核心数。例如在一个 8 核心的机器上,调度器会把 Go 代码同 时调度到 8 个 OS 线程上
- Go 语言中可以通过 runtime.GOMAXPROCS()函数设置当前程序并发时占用的 CPU 逻辑核心 数
- Go1.5 版本之前,默认使用的是单核心执行。Go1.5 版本之后,默认使用全部的 CPU 逻辑核 心数
//获取当前计算机上面的 Cup 个数
cpuNm := runtime.NumCPU()
fmt.Printl("cpuNm=", cpuNm)
//可以自己设置使用多个 cpu
runtime.GOMAXPROCS(cpuNm - 1)
计算1-240000的素数
import (
"fmt"
"sync"
"testing"
"time"
)
var wg sync.WaitGroup
func Num(n int) {
defer wg.Done()
var start = (n-1)*15000 + 1
var end = n * 15000
for i := start; i < end; i++ {
if n > 1 {
var flag = true
for j := 2; j < i; j++ {
if i%j == 0 {
flag = false
break
}
}
if flag {
//fmt.Printf("%v is 素数\n",i)
}
}
}
}
func TestNum(t *testing.T) {
var startTime = time.Now().Unix()
for i := 1; i <= 8; i++ {
wg.Add(1)
go Num(i)
}
wg.Wait()
var endTime = time.Now().Unix() - startTime
fmt.Println(endTime) //1到2s cpu 4核心 8 线程
}