概念
- 并发 某个时间段内同时执行。比如单行CPU通过切换执行不同的应用来实现并发。
- 并行 某个时间点同时执行。单核CPU不可能实现并行。
查看当前程序的并发数量
go
package main
import "fmt"
import "runtime"
import "time"
func main(){
// 获取【逻辑CPU核心数】(含超线程)
// 4核8线程 → 返回8
var nums = runtime.NumCPU()
fmt.Println("线程数量",nums)
// 获取当前系统:linux / windows / darwin
var sysname = runtime.GOOS
fmt.Println("系统",sysname)
// 获取Go最大并行GOMAXPROCS(默认=CPU核心数)
// 代表:最多同时跑多少个工作线程
var maxThreads = runtime.GOMAXPROCS(0)
fmt.Println("最大可用线程数量", maxThreads)
// 当前goroutine数量(初始只有main)
var goroutineNum = runtime.NumGoroutine()
fmt.Println("goroutineNum",goroutineNum) // 1
// 启动2个goroutine
go Sleep1()
go Sleep1()
// 刚用go关键字,立刻计数+2 → 结果=3
goroutineNum = runtime.NumGoroutine()
fmt.Println("goroutineNum",goroutineNum) // 3
// 打印所有goroutine的栈信息
buf := make([]byte, 1<<20) // 分配1MB缓冲区
length := runtime.Stack(buf, true) // 获取全部goroutine栈
stackInfo := string(buf[:length])
fmt.Println("======== 全部栈信息 ========")
fmt.Println(stackInfo)
fmt.Println("============================")
// 主线程长时间休眠,不让程序退出
time.Sleep(time.Second * 20000)
}
// 睡眠10秒的goroutine
func Sleep1 (){
time.Sleep(time.Second * 10)
}
修改线程数量
css
package main
import "fmt"
import "runtime"
func main() {
// 参数是0表示读取
a := runtime.GOMAXPROCS(0)
fmt.Println(a)
// 设置2个线程
runtime.GOMAXPROCS(2)
a = runtime.GOMAXPROCS(0)
fmt.Println(a)
// 虽然可以设置多个线程,但是不推荐,用默认的就好
runtime.GOMAXPROCS(100)
a = runtime.GOMAXPROCS(0)
fmt.Println(a)
}
goroutine的使用
go
package main
import "fmt"
import "time"
func SayHello(index int) {
for {
fmt.Println("Hello", index)
time.Sleep(time.Second)
}
}
func main() {
go SayHello(1)
go SayHello(2)
go func(index int) {
for {
fmt.Println("main hello ", index)
time.Sleep(time.Second)
}
}(3)
var input int
fmt.Scanln(&input)
}
atomic
dart
var num atomic.Uint64
num.Store(10) // 存值
v := num.Load() // 取值
num.Add(1) // 原子+1
sync.WaitGroup
Add(n)开启了n个协程Done()计数器减1Wait()等待所有的协程结束
go
package main
import "fmt"
import "sync"
import "time"
func SayHello(tag string,wg *sync.WaitGroup){
defer func(){
wg.Done()
}()
for i:=0;i<10;i++{
fmt.Println(tag,"hello")
time.Sleep(time.Second)
}
}
func main(){
wg := sync.WaitGroup{}
wg.Add(2)
go SayHello("Coroutine1", &wg)
go SayHello("Coroutine2", &wg)
//等待协程结束
fmt.Println(time.Now().Unix())
wg.Wait()
fmt.Println(time.Now().Unix())
}
共享数据的通信
csharp
package main
import (
"fmt"
"sync"
"runtime"
)
var total int = 0
func Add1(lock *sync.Mutex){
lock.Lock()
total += 1
lock.Unlock()
}
func main(){
lock := &sync.Mutex{}
for i:=0;i<20;i++{
go Add1(lock)
}
for{
lock.Lock()
value := total
lock.Unlock()
fmt.Println(value)
runtime.Gosched()
if value > 10 {
break
}
}
}
channel
go
// chan int是一个类型
var a chan int
// 不带缓存的channel,每次写入都阻塞
var a = make(chan int)
// 带缓存的channel,性能会更好
var a = make(chan int, 10)
//写入结束,调用close关闭通道
close(a)
// 默认chan是可以写,也可以读取
var aRead <-chan int = a
var aWrite chan<- int = a
go
package main
import "fmt"
import "time"
import "sync"
func Add1(a chan int, wg *sync.WaitGroup) {
defer wg.Done()
for value := range a {
fmt.Println(value)
}
}
func Sub1(a chan int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 5; i++ {
a <- 100
fmt.Println("sub1 <- 100")
time.Sleep(time.Second)
}
close(a)
}
func main() {
wg := sync.WaitGroup{}
wg.Add(2)
var a = make(chan int)
go Sub1(a, &wg)
fmt.Println("此时,channel被阻塞")
time.Sleep(time.Second * 2)
go Add1(a, &wg)
wg.Wait()
}
chan的关闭
go
package main
import "fmt"
import "time"
func Consume(c *chan int) {
defer fmt.Println(" consume finish")
for {
// 这里无法判断chan是否关闭
//content := <- *c
//fmt.Println("content is ", content)
content, ok := <-*c
if ok {
fmt.Println("1", content, ok)
} else {
// 2 0 false content是0,也可能是chan传递过来的就是0,所以必须>用ok来判断是否被关闭
fmt.Println("2", content, ok)
break
}
}
}
func Producer(c *chan int) {
defer func() {
close(*c)
fmt.Println("producer finish")
}()
*c <- 100
*c <- 200
}
func main() {
a := make(chan int, 10)
go Consume(&a)
go Producer(&a)
time.Sleep(time.Hour)
}