//控制并发数量
func GoroutineAnts() {
//1.统计当前goroutine数量
go func() {
for {
fmt.Println("goroutinenum:", runtime.NumGoroutine())
time.Sleep(250 * time.Millisecond)
}
}()
//2.初始化协程池 goroutine pool
size := 1024
pool, err := ants.NewPool(size)
if err != nil {
log.Fatalln(err)
}
//保证pool被关闭
defer pool.Release()
//3.利用pool,调度需要并发的大量goroutine
for {
//向pool中提交一个执行的goroutine
err := pool.Submit(func() {
V := make([]int, 1024)
_ = V
time.Sleep(time.Second * 100)
})
if err != nil {
log.Fatalln(err)
}
}
}
//控制并发数量
func GoroutineAnts() {
//1.统计当前goroutine数量
go func() {
for {
fmt.Println("goroutinenum:", runtime.NumGoroutine())
time.Sleep(250 * time.Millisecond)
}
}()
//2.初始化协程池 goroutine pool
size := 1024
pool, err := ants.NewPool(size)
if err != nil {
log.Fatalln(err)
}
//保证pool被关闭
defer pool.Release()
//3.利用pool,调度需要并发的大量goroutine
for {
//向pool中提交一个执行的goroutine
err := pool.Submit(func() {
V := make([]int, 1024)
_ = V
time.Sleep(time.Second * 100)
})
if err != nil {
log.Fatalln(err)
}
}
}
所有G由P管理,一旦P与M绑定,就将G交由M执行,注意:P只能管理G,不能执行G
//主动让出执行权
func GoroutineSched() {
wg := sync.WaitGroup{}
max := 100
wg.Add(2)
//设置为 1个P 在调度 G
runtime.GOMAXPROCS(1) //单线程模式
//输出奇数
go func() {
defer wg.Done()
for i := 1; i <= max; i += 2 {
fmt.Println(" ", i)
//time.Sleep(time.Millisecond * 3)
//主动让出
runtime.Gosched()
}
}()
//输出偶数
go func() {
defer wg.Done()
for i := 2; i <= max; i += 2 {
fmt.Println(" ", i)
//time.Sleep(3 * time.Millisecond) //延长时间,使得处理机进行调度 单处理机运行一般10ms
//主动让出
runtime.Gosched()
}
}()
wg.Wait()
}
1.需要先关闭,然后再循环遍历取值
//空接口管道断言
dog1:=<-allChan
a:=dog1.(dog) //需要类型断言
a:=(<-allChan).(dog) //与上面等价
var intChan chan int
func ReadAndWrite() {
intChan = make(chan int, 50)
exitChan := make(chan bool, 1)
go WriteData(intChan)
go ReadData(intChan, exitChan)
if <-exitChan {
fmt.Println("end!")
}
}
//封装两个方法
func WriteData(intChan chan int) {
rand.Seed(time.Now().UnixNano())
for i := 0; i < 50; i++ {
var tempInt int
tempInt = rand.Intn(4) + 10
fmt.Printf("写入数据为%v,第%v次写入\n", tempInt, i+1)
intChan <- tempInt
}
close(intChan)
}
func ReadData(intChan chan int, exitChan chan bool) {
var count int
for {
val, ok := <-intChan
count++
if !ok {
break
}
fmt.Printf("读取到%v,是第%v次读取\n", val, count)
}
exitChan <- true //读取完毕
close(exitChan)
}
// 检查是否为素数 通常用法
func IsPrime(n int) {
for i := 1; i <= n; i++ {
var flag bool = true
for j := 2; j < i; j++ {
if i%j == 0 {
flag = false
continue
}
}
if flag {
fmt.Println(i, "为素数")
}
}
}
// 检查是否为素数 管道协程
func initChan(n int) {
for i := 1; i <= n; i++ {
intChan <- i
}
close(intChan)
}
func isPrimeChannel(intChan chan int, primeChan chan int, exitChan chan bool) {
var flag bool
for {
intchan, ok := <-intChan
flag = true
if !ok {
break
}
for j := 2; j < intchan; j++ {
if intchan%j == 0 {
flag = false
continue
}
}
if flag {
primeChan <- intchan
}
}
exitChan <- true
}
func IsPrimeChannel() {
var primeChan chan int = make(chan int, 100)
var exitChan chan bool = make(chan bool, 8)
go initChan(100)
for i := 1; i <= 8; i++ {
go isPrimeChannel(intChan, primeChan, exitChan)
}
go func() {
for i := 0; i < 8; i++ {
<-exitChan
}
close(primeChan)
close(exitChan) //取完才能关闭,因为开启了八个协程,所以该管道会有八个值
}()
for {
res, ok := <-primeChan
if !ok {
break
}
fmt.Println("素数有", res)
}
}
break label 跳出循环不再执行for循环里的代码,即不再第二次执行for循环代码。
// 检查是否为素数 管道协程
func initChan(n int) {
for i := 1; i <= n; i++ {
intChan <- i
}
//close(intChan)
}
func isPrimeChannel(intChan chan int, primeChan chan int, exitChan chan bool) {
var flag bool
label:
for {
select {
case intchan := <-intChan:
flag = true
for j := 2; j < intchan; j++ {
if intchan%j == 0 {
flag = false
continue
}
}
if flag {
primeChan <- intchan
}
default:
break label
}
}
exitChan <- true
}
func IsPrimeChannel() {
var primeChan chan int = make(chan int, 100)
var exitChan chan bool = make(chan bool, 8)
go initChan(100)
for i := 1; i <= 8; i++ {
go isPrimeChannel(intChan, primeChan, exitChan)
}
go func() {
for i := 0; i < 8; i++ {
<-exitChan
}
//close(primeChan)
//close(exitChan) //取完才能关闭,因为开启了八个协程,所以该管道会有八个值
}()
label:
for {
select {
case res := <-primeChan:
fmt.Println("素数有", res)
default:
break label
}
}
}
//defer recover panic
func PanicMain() {
go onlyOut(2)
fmt.Println("exit main")
go onlyIn()
time.Sleep(time.Second * 2)
}
func onlyOut(num int) {
defer func() {
if err := recover(); err != nil {
fmt.Println("onlyOut报错", err)
}
}()
var testMap map[int]int //这地方报错
testMap[0] = 1
}
func onlyIn() {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}
//生产者消费者
func ProCouMain() {
num := 10
storageChan := make(chan Product, num)
shopChan := make(chan Product, num)
exitChan := make(chan bool, 1)
for i := 0; i < num; i++ {
go Produce(storageChan, num)
}
go Logistics(storageChan, shopChan)
go Consumer(shopChan, num, exitChan)
if <-exitChan {
return
}
}
//定义商品
type Product struct {
Name string
}
//生产
func Produce(storage chan<- Product, count int) {
for {
producer := Product{Name: "商品:" + strconv.Itoa(count)}
storage <- producer
count--
fmt.Println("生产了:", producer)
time.Sleep(time.Second)
if count < 1 {
return
}
}
}
//运输
func Logistics(storageChan <-chan Product, shopCHan chan<- Product) {
for {
product := <-storageChan
shopCHan <- product
fmt.Println("运输了", product)
}
}
//消费者消费
func Consumer(shopChan <-chan Product, count int, exitChan chan<- bool) {
for {
product := <-shopChan
fmt.Println("消费者消费了", product)
count--
if count < 1 {
exitChan <- true
return
}
}
}
//协程管道定时任务
func TimingMain() {
//方式一:
fmt.Println("当前时间", time.Now())
timer := time.NewTimer(time.Second * 3)
t1 := <-timer.C //timer.c是一个只读的管道
fmt.Println("现在时间", t1)
//方式二:
fmt.Println("当前时间", time.Now())
t2 := <-time.After(time.Second * 3) //源码可见,实际返回的是 return NewTimer(d).C
fmt.Println("现在时间", t2)
}
//协程管道定时任务
func TimingMain() {
//方式一:
fmt.Println("当前时间", time.Now())
timer := time.NewTimer(time.Second * 3)
if flag {
timer.Stop()
} else {
t1 := <-timer.C //timer.c是一个只读的管道
fmt.Println("现在时间", t1)
}
//方式二:
//fmt.Println("当前时间", time.Now())
//t2 := <-time.After(time.Second * 3) //源码可见,实际返回的是 return NewTimer(d).C
//fmt.Println("现在时间", t2)
}
func IsStopTimer() bool {
rand.Seed(time.Now().UnixNano())
tempInt := rand.Intn(2) + 16
if tempInt >= 18 {
fmt.Println("已经找到大于18的,结束timer")
return true
} else {
return false
}
}
//循环时钟
func CircMain() {
var count int = 0
var exitChan chan bool
newTicker := time.NewTicker(time.Second)
go func() {
for {
t := <-newTicker.C
fmt.Println(t.Format("2006-01-02 03:04:05PM"))
count++
if count > 1 {
newTicker.Stop()
exitChan <- true
}
}
}()
<-exitChan
fmt.Println("结束!")
}
//循环时钟
func CircMain() {
var wg sync.WaitGroup
wg.Add(2)
var count int = 0
var countb int = 0
newTicker := time.NewTicker(time.Second)
go func() {
defer wg.Done()
defer newTicker.Stop()
for {
t := <-newTicker.C
fmt.Println(t.Format("ticker" + "2006-01-02 03:04:05PM"))
count++
if count > 2 {
return
}
}
}()
newTimer := time.NewTimer(time.Second)
go func() {
defer wg.Done()
defer newTimer.Stop()
for {
t := <-newTimer.C
fmt.Println(t.Format("timer:" + "2006-01-02 03:04:05PM"))
newTimer.Reset(time.Second)
countb++
if countb > 3 {
return
}
}
}()
wg.Wait()
fmt.Println("结束!")
}
//context主动取消
func ContextCancel() {
//1.创建cancelContext
ctx, cancel := context.WithCancel(context.Background())
//2.启动goroutine 携带cancelCtx
for i := 0; i < 4; i++ {
//启动goroutine 携带ctx参数
go func(c context.Context, n int) {
//监听context的取消完成channel,来确定是否执行了主动的cancel操作
for {
select {
//等待接收c.Done()这个channel
case <-c.Done():
fmt.Println("1 context cancel!") //这里不输出原因是 37行 cancel()执行后 主goroutine已经结束,因此后续goroutine自动结束,所以还没来得及执行就结束了。
return //不是<-c.Done()导致协程结束 ,是因为return导致结束。 <-c.Done()只是一个开关
default:
}
fmt.Println(strings.Repeat(" ", n), n)
time.Sleep(time.Millisecond * 300)
}
}(ctx, i)
}
//3.主动取消 cacel()
//3秒后取消
select {
//case <-time.NewTimer(3 * time.Second).C:
case <-time.After(2 * time.Second):
cancel() // ctx.Done() <- struct{}
}
select {
case <-ctx.Done():
fmt.Println("2 context cancel!")
}
}
//定时取消信号
func ContextCancelTime() {
fmt.Println(time.Now())
//1.创建 带有时间的 cancelContext 一段时间后取消
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
//ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second*2)) //以上两个等价 这个是具体时间点
//或具体到年月日时分秒纳秒,
//curr := time.Now()
//ctx, cancel := context.WithDeadline(context.Background(), time.Date(curr.Year(), curr.Month(), curr.Day(), 20, 30, 0, 0, time.Local))
//2.启动goroutine 携带cancelCtx
for i := 0; i < 4; i++ {
//启动goroutine 携带ctx参数
go func(c context.Context, n int) {
//监听context的取消完成channel,来确定是否执行了主动的cancel操作
for {
select {
//等待接收c.Done()这个channel
case <-c.Done():
fmt.Println("1 context cancel!") //这里不输出原因是 37行 cancel()执行后 主goroutine已经结束,因此后续goroutine自动结束,所以还没来得及执行就结束了。
return //不是<-c.Done()导致协程结束 ,是因为return导致结束。 <-c.Done()只是一个开关
default:
}
fmt.Println(strings.Repeat(" ", n), n)
time.Sleep(time.Millisecond * 300)
}
}(ctx, i)
}
//3.主动取消 cancel() 到时取消
select {
//4s后主动取消
case <-time.After(4 * time.Second):
cancel() // ctx.Done() <- struct{}
fmt.Println("主动", time.Now())
fmt.Println("call cancel() Cancel")
case <-ctx.Done(): //2s后到时取消
fmt.Println("被动", time.Now())
fmt.Println("main cancel!")
}
}
//cancel取消向下传递
func ContextCancelDeep() {
//1.创建层级关系的cancelCtx 1-(2-(4),3)
//ctxOne, _ := context.WithCancel(context.Background())
//ctxTwo, cancel := context.WithCancel(ctxOne)
//ctxThree, _ := context.WithCancel(ctxOne)
//ctxFour, _ := context.WithCancel(ctxTwo)
//1.定时器方式保证取消 一方面也解决了枷锁固定问题
ctxOne, _ := context.WithTimeout(context.Background(), time.Second*2)
ctxTwo, cancel := context.WithTimeout(ctxOne, time.Second*2)
ctxThree, _ := context.WithTimeout(ctxOne, time.Second*2)
ctxFour, _ := context.WithTimeout(ctxTwo, time.Second*2)
wg := sync.WaitGroup{}
//2.使用goroutine来接收ctx.Done()
go func(c context.Context) {
wg.Add(1)
defer wg.Done()
select {
case <-c.Done(): //ctxOne
fmt.Println("one cancel")
}
}(ctxOne)
go func(c context.Context) {
wg.Add(1)
defer wg.Done()
select {
case <-c.Done(): //ctxOne
fmt.Println("two cancel")
}
}(ctxTwo)
go func(c context.Context) {
wg.Add(1)
defer wg.Done()
select {
case <-c.Done(): //ctxOne
fmt.Println("three cancel")
}
}(ctxThree)
go func(c context.Context) {
wg.Add(1)
defer wg.Done()
select {
case <-c.Done(): //ctxOne
fmt.Println("four cancel")
}
}(ctxFour)
//主动取消
cancel()
wg.Wait()
}
//context传值
func ContextValue() {
wg := sync.WaitGroup{}
wg.Add(1)
//1.创建带有value的Context
//传递 有哪个用哪个 没有的话就用上层的(如果key一致),key不一致的话,按从低到高找 1--2--3
//key的值不建议直接使用string或其他内置类型 例如 两个包的 key "one" ,但值不一样
ctxOne := context.WithValue(context.Background(), MyContextString("one"), "go of one")
ctxTwo := context.WithValue(ctxOne, "title", "go of two")
ctxThree := context.WithValue(ctxTwo, "title", "go of three")
//2.将ctx传到goroutine中使用
go func(c context.Context) {
defer wg.Done()
//获取key对应的value
if v := c.Value(MyContextString("one")); v != nil {
fmt.Println("title 对应的值为", v)
return
}
fmt.Println("NOT FOUND VALUE ACCORDING TO title")
}(ctxThree)
wg.Wait()
}
func SyncRLock() {
//RLOCK可能先于LOCK
wg := sync.WaitGroup{}
//模拟多个goroutine
var rwlck sync.RWMutex
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
//输出一段内容
//rwlck.Lock()
rwlck.RLock()
fmt.Println(time.Now())
time.Sleep(1 * time.Second)
//rwlck.Unlock()
rwlck.RUnlock()
}()
}
wg.Add(1)
go func() {
defer wg.Done()
defer wg.Done()
//输出一段内容
rwlck.Lock()
fmt.Println(time.Now(), "LOCK")
time.Sleep(1 * time.Second)
rwlck.Unlock()
}()
wg.Wait()
}
//syncmap
func SyncSyncMapMethod() {
wg := sync.WaitGroup{}
//直接初始化即可使用,不需要指定key和value的类型
var m sync.Map
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
//存储
m.Store(n, fmt.Sprintf("value:(%d)", n))
}(i)
}
//并发goroutine完成load操作
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
//读取
fmt.Println(m.Load(n))
}(i)
}
wg.Wait()
//遍历
m.Range(func(key, value interface{}) bool {
fmt.Println(key, value)
//返回true,继续遍历,直到map结尾
//返回false 提前终止遍历
return true
})
m.Delete(4)
}
// 原子操作
func SyncAtomicAdd() {
//并发的过程 没有加锁
//atomic 原子的int32 counter:=0
var counter atomic.Int32
wg := sync.WaitGroup{}
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 100; i++ {
//原子累加 == counter++
counter.Add(1)
}
}()
}
wg.Wait()
fmt.Println("counter", counter.Load())
}
func SyncAtomicValue() {
//一个goroutine动态加载配置
//配置为自定义类型 map[string]string
//模拟加载配置,例如从配置文件加载,返回解析的配置信息
var loadConfig = func() map[string]string {
return map[string]string{
//some config
"title": "马士兵go并发",
"varConf": fmt.Sprintf("%d", rand.Int31()),
}
}
//config的操作应该是并发安全的,我们选择使用原子操作来实现
//自定义的原子操作类型,atomic.Value
var config atomic.Value
//每n秒加载一次配置文件(监视配置文件的修改)
go func() {
for {
//原子加载配置
config.Store(loadConfig())
fmt.Println("lasted config was loaded", time.Now().Format("15:04:05.99999999"))
time.Sleep(time.Second)
}
}()
//在多个goroutine中使用最新配置
//不能在加载的过程中使用配置
for {
go func() {
//原子加载配置
c := config.Load()
fmt.Println(c, time.Now().Format("15:04:06.99999999"))
}()
time.Sleep(400 * time.Millisecond)
}
select {}
}
// sync.pool
//并发资源管理
func SyncPool() {
//管理资源 资源为可复用的临时对象时,使用池子
//原子计数器
var counter int32 = 0
//定义元素的Newer,创建器
elementNewer := func() any {
//原子的计数器累加
atomic.AddInt32(&counter, 1)
//池中元素推荐(强烈)是指针类型
return new(bytes.Buffer)
}
//Pool的初始化
pool := sync.Pool{
New: elementNewer, //一旦池子里没有的话,调用pool.New
}
//并发的申请和交回元素
wg := sync.WaitGroup{}
workNum := 1024
wg.Add(workNum)
for i := 0; i < workNum; i++ {
go func() {
defer wg.Done()
//申请元素,通常需要断言为特定类型
buffer := pool.Get().(*bytes.Buffer)
//交回元素
defer pool.Put(buffer)
//使用元素
_ = buffer.String()
}()
}
wg.Wait()
//测试创建元素次数
fmt.Println("elements number:", counter)
}
package main
import (
"fmt"
"sync"
)
func main() {
wg := sync.WaitGroup{}
num := 1000
var counter int = 0
wg.Add(num)
for i := 0; i < num; i++ {
go func() {
defer wg.Done()
//累加
for k := 0; k < 100; k++ {
counter++
}
}()
}
wg.Wait()
fmt.Println("counter number is ", counter)
}
func SyncOnce() {
//初始化config变量
config := make(map[string]string)
//1.初始化 sync.Once
once := sync.Once{}
//加载配置函数
loadConfig := func() {
//2. 利用 once.Do()来执行
once.Do(func() {
//保证只执行一次
config = map[string]string{
"varInt": fmt.Sprintf("%d", rand.Int31()),
}
fmt.Println("config loaded")
})
}
//模拟多个goroutine,多次调用加载配置
//测试加载配置操作,执行了几次
workers := 10
wg := sync.WaitGroup{}
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
defer wg.Done()
//并发的多次加载配置
loadConfig()
//使用配置
_ = config
}()
}
wg.Wait()
}
// 一个goroutine接收数据,多个goroutine处理数据
func SyncCond() {
wg := sync.WaitGroup{}
//全局(使用cond的gouroutine来说的)数据
var data []int
dataLen := 1024 * 1024
//1.创建sync.Cond
cond := sync.NewCond(&sync.Mutex{})
//接受数据goroutine,一个
wg.Add(1)
go func() {
defer wg.Done()
cond.L.Lock() //用于输出 “cond broadcast”
defer cond.L.Unlock()
for i := 0; i < dataLen; i++ {
data = append(data, i*i) //模拟数据传输
}
//2.在数据接收完毕后,在通知处理的goroutine进行数据处理
//广播,可以选的需要锁定
time.Sleep(time.Second * 2)
cond.Broadcast() //先 wait 再 broadcast
fmt.Println("cond broadcast")
}()
const workers = 8
wg.Add(workers)
//处理数据的goroutine,一组
for i := 0; i < workers; i++ {
go func() {
defer wg.Done()
//3.在数据未接收完之前(就是等待的条件),等待
//wait前要加锁
cond.L.Lock()
//defer cond.L.Unlock()
for len(data) < dataLen { //表明没有传输完毕,等待中 使用 for 是因为其他地方可能使用了 cond.Broadcast(),提前释放了wait
cond.Wait() //函数里先解锁了,后加锁了
}
//处理数据
fmt.Println("处理数据,数据长度:", len(data))
//wait后要解锁,业务逻辑处理完毕
cond.L.Unlock()
}()
}
wg.Wait()
}
// WalkDir 外部调用的遍历目录统计信息的方法
func WalkDir(dirs ...string) string { //多个目录,一个目录下有多个文件
//一、保证至少有一个目录需要统计遍历
//默认为当前目录
if len(dirs) == 0 {
dirs = []string{"."}
}
//二、初始化变量,channel用于完成Size的传递,WaitGroup用于等待调度
filesizeCh := make(chan int64, 1)
wg := &sync.WaitGroup{} //在多个协程传递用指针类型
//三、启动多个Goroutine统计信息,取决于len(dirs)
for _, dir := range dirs {
wg.Add(1) //+1
//并发的遍历统计每个目录的信息
go walkDir(dir, filesizeCh, wg)
}
//四、启动累计运算的Goroutine
//1.用户关闭 filesizeCh
go func(wg *sync.WaitGroup) {
//等待统计工作的完成
wg.Wait()
//关闭 filesizeCh
close(filesizeCh)
}(wg)
//2.range的方式从 filesizeCh 中获取文件大小
//3.将统计结果,用channel传递出来
fileNumCh := make(chan int64, 1)
sizeTotalCh := make(chan int64, 1)
go Total(filesizeCh, fileNumCh, sizeTotalCh)
//五、整理返回值
//size的单位 byte
//需要的单位 mb
result := fmt.Sprintf("%d files %.2f MB\n", <-fileNumCh,
float64(<-sizeTotalCh)/1e6) //1024*1024
return result
}
// 遍历并统计某个特定目录的信息
// 核心实现函数,完成递归,统计
func walkDir(dir string, filesizeCh chan<- int64, wg *sync.WaitGroup) {
//一、wg计数器减少
defer wg.Done()
//二、读取 dir 下的全部文件信息
for _, fileinfo := range fileInfos(dir) {
//三、根据 dir 下的文件信息
if fileinfo.IsDir() {
//1.如果是目录, 获取递归信息
//子目录地址
subDir := filepath.Join(dir, fileinfo.Name())
//递归调用,也是并发的,也需要wg统计
wg.Add(1)
go walkDir(subDir, filesizeCh, wg)
} else {
//2.如果不是,就是文件,统计文件大小,放入channel
filesizeCh <- fileinfo.Size() //Byte
}
}
}
//获取某个目录下全部信息列表
func fileInfos(dir string) []fs.FileInfo {
//一、读取目录的全部文件
entries, err := os.ReadDir(dir)
if err != nil {
log.Println("WalkDir error ", err)
return []fs.FileInfo{} //nil
}
//二、获取文件的文件信息
//DirEntry to FileInfo
infos := make([]fs.FileInfo, 0, len(entries))
for _, entry := range entries {
//如果获取文件信息无错误,存储到Infos中
if info, err := entry.Info(); err == nil {
infos = append(infos, info)
}
}
//三、返回
return infos
}
func Total(filesizeCh <-chan int64, fileNumCh, sizeTotalCh chan<- int64) {
//统计文件数,和文件整体大小
var fileNum, sizeTotal int64
//遍历全部 filesizeCh 元素,统计文件数量和大小,直到 filesizeCh 通道关闭
for filesize := range filesizeCh { //使用 for 是防止通道阻塞
//累计文件数,和统计文件整体大小
fileNum++
sizeTotal += filesize
}
//将统计结果写入channel
fileNumCh <- fileNum
sizeTotalCh <- sizeTotal
}