go并发编程

go并发编程

1. 任务调度

1.1 sync .WaitGroup

package main

import (
	"fmt"
	"sync"
)

// Goroutine并发编程
func goRoutineDemo() {
	fmt.Println("GoRoutineDemo...")
}

var (
	wg sync.WaitGroup  // 类比java:countDownLatch
)

func goRoutineWg(i int) {
	defer wg.Done() // 任务数-1
	fmt.Println(i, "goRoutineWg...")
}

func main() {
	// 单个任务
	go goRoutineDemo()
	// 多个任务同时进行 使用sync.WaitGroup + go func 方式
	for i := 0; i < 10; i++ {
		wg.Add(1)  // 任务数+1 
		go goRoutineWg(i)
	}

	wg.Wait() // 阻塞等待所有任务完成
	fmt.Println("GoRoutineMain...")
}
runOut:
	GoRoutineDemo...
	9 goRoutineWg...
	3 goRoutineWg...
	4 goRoutineWg...
	5 goRoutineWg...
	6 goRoutineWg...
	7 goRoutineWg...
	8 goRoutineWg...
	1 goRoutineWg...
	0 goRoutineWg...
	2 goRoutineWg...
	GoRoutineMain...

1.2 sync.Once

package main

import (
	"fmt"
	"sync"
)

var (
	so sync.Once
)

func load() {
	fmt.Println("初始化完成...")
}

func initOper() {
	//so.Do(load) // 只执行一次 多用于初始化操作
	so.Do(func() {
		load()
	})
}

func main() {
	initOper()
	initOper()
}
runOut:
	初始化完成...

1.3 sync.Cond

package main

import (
	"fmt"
	"sync"
	"time"
)

/**
sync.Cond 表示的是条件变量,用来协调多个 goroutine之间的同步。当条件满足 通过Signal或者Broadcast方法唤醒挂起的任务。
Wait(): 挂起所有goroutine任务 直到条件满足;调用Wait前 优先调用L.Lock加锁。
Signal(): 通知一个等待的goroutine任务。
Broadcast():通知所有等待的goroutine任务。
*/

var (
	ok bool  // 任务标识
)

func producer(cond *sync.Cond) {
	fmt.Println("producer start")
	time.Sleep(time.Millisecond * 10) // 保证消费者任务挂起等待(加锁)
	cond.L.Lock()
	time.Sleep(time.Millisecond * 30)
	ok = true
	fmt.Println("producer end")
	cond.L.Unlock()
	cond.Broadcast() // 唤醒所有挂起的消费者任务
	//cond.Signal() // 唤醒1个挂起的消费者任务(若任务耗时大于主程序等待耗时 会导致任务丢失)
	fmt.Println("唤醒等待任务>>>")
}

func consumer(name string, cond *sync.Cond) {
	fmt.Printf("consumer-%s start\n", name)
	cond.L.Lock()
	for !ok {
		fmt.Printf("consumer-%s wait\n", name)
		cond.Wait() // 挂起当前任务 直到调用Signal或Broadcast唤醒
	}
	fmt.Printf("consumer-%s work\n", name)
	time.Sleep(time.Millisecond * 10) // 模拟消费者任务耗时
	fmt.Printf("consumer-%s end\n", name)
	defer cond.L.Unlock()
}

func main() {
	cond := sync.NewCond(&sync.Mutex{}) // 实例化 Locker入参为sync.Mutex或者sync.RWMutex
	taskLi := []string{"A", "B"}  // 任务集
	for _, v := range taskLi {
		go consumer(v, cond)
	}
	go producer(cond)
	time.Sleep(time.Second) // 等待任务结束
	fmt.Println("程序执行结束!!!")
}
runOut:
    producer start
    consumer-A start
    consumer-A wait 
    consumer-B start
    consumer-B wait 
    producer end
    唤醒等待任务>>>
    consumer-A work
    consumer-A end
    consumer-B work
    consumer-B end
    程序执行结束!!!

1.4 runtime

package main

import (
	"fmt"
	"runtime"
	"time"
)


// runtime
func countTask(count int) {
	for i := 0; i < count; i++ {
		if i == 2 {
			defer fmt.Println("直接退出洗衣服任务")
			runtime.Goexit() // 强制中止当前任务 不影响其它任务
		}
		fmt.Println("洗衣服:", i)
		runtime.Gosched() // 当前任务挂起、其它任务执行

	}
}

func cookTask(count int) {
	for i := 0; i < count; i++ {
		if i == 2 {
			defer fmt.Println("直接退出做饭任务")
			runtime.Goexit()
		}
		fmt.Println("做饭:", i)
		runtime.Gosched() // 当前任务挂起、其它任务执行

	}
}

func main() {
	runtime.GOMAXPROCS(1) //CPU核心数
	taskNum := 5          // 任务数
	go countTask(taskNum)
	go cookTask(taskNum)

	time.Sleep(time.Second * 2)  // 等待任务执行完毕
	fmt.Println("所有任务结束...")
}
runOut:
    做饭: 0
    洗衣服: 0         
    做饭: 1           
    洗衣服: 1         
    直接退出做饭任务  
    直接退出洗衣服任务
    所有任务结束...

1.5 channel

package main

import (
	"fmt"
	"time"
)

func taskB(ch <-chan int) {
	fmt.Println("B start")
	<-ch  // 阻塞等待
	fmt.Println("B end")
}

func taskA(ch chan int) {
	defer close(ch)
	fmt.Println("A start")
	time.Sleep(time.Millisecond * 100) // 模拟耗时
	fmt.Println("A end")
}

func main() {
	/**
	无缓冲管道: 需要接收和发送同时准备 且阻塞
	有缓冲管道: 有缓冲空间则不阻塞
	单向管道: 只发送或接收
	*/
	ch := make(chan int) // 通过管道协调任务调度顺序 并通过关闭通知所有等待的任务

	go taskB(ch)
	go taskB(ch)
	go taskA(ch)

	time.Sleep(time.Second) // 模拟等待任务结束
}
runOut:
    A start
    B start
    B start
    A end
    B end
    B end


2. 数据通信

2.1 channel

package main

import (
	"fmt"
	"time"
)

func main() {

	ch := make(chan int, 5) // 带缓冲通道

	go func(ch chan int) {
		for i := 0; i < 5; i++ {
             fmt.Println("send:", i)
			ch <- i
		}
		defer close(ch)
	}(ch) // 发送

	go func(ch chan int) {
		for v := range ch {
			fmt.Println("receiveA:", v)
		}
	}(ch) // 接收1

	go func(ch chan int) {
		for v := range ch {
			fmt.Println("receiveB:", v)
		}
	}(ch) // 接收2

	time.Sleep(time.Second) // 等待协程任务完成
}
runOut:
	send: 0
    send: 1    
    send: 2    
    send: 3    
    send: 4    
    receiveA: 1
    receiveA: 2
    receiveA: 3
    receiveA: 4
    receiveB: 0

2.2 channel + select

package main

import (
	"fmt"
	"time"
)

func main() {
	ch1 := make(chan int, 1)
	ch2 := make(chan int, 1)

	// select 同时监听多个管道情况 且只处理未阻塞的 当多个同时未阻塞 则随机处理
	go func(ch chan int) {
		time.Sleep(time.Millisecond * 200)
		ch <- 11
		defer close(ch)
	}(ch1)

	go func(ch chan int) {
		time.Sleep(time.Millisecond * 500)
		ch <- 111
		defer close(ch)
	}(ch2)

	for i := 0; i < 10; i++ {
		select {
		case d, ok := <-ch1:
			if ok {
				fmt.Println("ch1接收到数据:", d)
			} else {
				fmt.Println("ch1已关闭")
			}
		case d, ok := <-ch2:
			if ok {
				fmt.Println("ch2接收到数据:", d)
			} else {
				fmt.Println("ch2已关闭")
			}
		//case <-time.After(time.Millisecond * 600):
		//	fmt.Println("超时机制执行")
		default:
			fmt.Println("default执行")
		}

		time.Sleep(time.Millisecond * 200)
	}
}
runOut:
    default执行
    default执行
    ch1接收到数据: 11
    ch1已关闭
    ch2接收到数据: 111
    ch1已关闭
    ch2已关闭
    ch2已关闭
    ch2已关闭
    ch1已关闭

2.3 sync.Mutex

package main

import (
	"fmt"
	"sync"
)

var (
	sum = 0  // 全局变量
)

func main() {
	lock := sync.Mutex{}  // 锁(互斥锁、读写锁)
	wg := sync.WaitGroup{}

	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			lock.Lock() // 加锁
			sum++
			lock.Unlock() // 解锁
		}()
	}

	wg.Wait() // 等待所有任务执行完成
	fmt.Println("sum=", sum)

}
runOut:
	sum=100

2.4 atomic

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

var count int64 // 全局变量

func main() {
	/**
	sync.atomic 保证特定类型同一时刻的原子性操作
	类型:int32|int64|uint32|uint64|uintptr|unsafe.Pointer
	操作:
		读取 eg:LoadInt32(addr int32)(val int32)
		写入 eg:StoreInt32(addr *int32, val int32)
		修改 eg:AddInt32(addr *int32, delta int32)(new int32)
		交换 eg:SwapInt32(addr *in32, new int32)(old int32)
		比较交换 eg:CompareAndSwapInt32(addr *32, old, new int32)(swapped bool)
	*/
	var wg = sync.WaitGroup{}
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			atomic.AddInt64(&count, 1) // 原子操作
		}()
	}

	wg.Wait()
	fmt.Println("count=", count)

}
runOut:
	count=100

2.5 sync.Map

package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	sMap sync.Map
	iMap sync.Map
	wig  sync.WaitGroup
)

func main() {
	/**
	sync.Map 并发安全 读多写少 读无锁写加锁
	操作:
		读取:Load(key any)(value any, ok bool) 根据key获取value 存在返回value 不存在返回nil
		写入:Store(key,value any): 新增或更新
		查找写入:LoadOrStore(key,value any)(actual any, loaded bool): 若key存在则返回value, loaded为true;若key不存在则写入value,loaded为false
		删除:Delete(key any): 删除key
		获取并删除: LoadAndDelete(key any): 获取key对应的value值并删除key
		比较删除: CompareAndDelete(key, old any) bool
		比较交换: CompareAndSwap(key, old, new any) bool
		交换: Swap(key, value any) (any, bool)
		遍历: Range(f func(key,value any) bool): 遍历map key和val
	*/
	fmt.Println("操作-sync.Map")
	key := "name"
	val := "sync.Map"
	sMap.Store(key, val)
	fmt.Printf("map Store: key:%s->val:%s\n", key, val)

	v, ok := sMap.Load(key)
	fmt.Printf("map Load: key:%s->val:%s;ok:%t\n", key, v, ok)

	newKey := "age"
	newVal := "10"
	v, ok = sMap.LoadOrStore(newKey, newVal)
	fmt.Printf("map LoadOrStore: key:%s->val:%s;ok:%t\n", key, v, ok)

	sMap.Delete(newKey)
	fmt.Printf("map Delete: key:%s->val:%s\n", newKey, newVal)

	v, ok = sMap.LoadAndDelete(key)
	fmt.Printf("map LoadAndDelete: key:%s->val:%s\n", key, val)

	for i := 0; i < 5; i++ {
		sMap.Store(i, i)
	}
	sMap.Range(func(k, v any) bool {
		fmt.Printf("map Range: key:%v->val:%v\n", k, v)
		return true
	})

	fmt.Println("并发-sync.Map")
	wig.Add(3)
	go func() {
		for i := 0; i < 3; i++ {
			iMap.Store(i, i)
		}
		defer wig.Done()
	}()
	time.Sleep(time.Millisecond * 300) // 优先写完

	go func() {
		iMap.Range(func(k, v any) bool {
			fmt.Println("readA:", k, v)
			return true
		})
		defer wig.Done()
	}()

	go func() {
		iMap.Range(func(k, v any) bool {
			fmt.Println("readB:", k, v)
			return true
		})
		defer wig.Done()
	}()

	wig.Wait()
}
runOut:
    操作-sync.Map
    map Load: key:name->val:sync.Map;ok:true
    map LoadOrStore: key:name->val:10;ok:false
    map Delete: key:age->val:10
    map LoadAndDelete: key:name->val:sync.Map
    map Range: key:0->val:0
    map Range: key:1->val:1
    map Range: key:2->val:2
    map Range: key:3->val:3
    map Range: key:4->val:4
    并发-sync.Map
    readB: 0 0
    readB: 1 1
    readB: 2 2
    readA: 0 0
    readA: 1 1
    readA: 2 2

2.6 sync.Pool

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

var (
	created   int32
	wcg       sync.WaitGroup
	workerNum = 5
)

func main() {
	/**
	sync.Pool Get、Put并发安全  New不确定是否安全   通过New实例对象 Get从Pool获取对象 Put释放对象到Pool
	临时对象缓存、增加临时对象的重用率、减少内存碎片
	参考: https://zhuanlan.zhihu.com/p/591805870
	*/

	pool := &sync.Pool{New: func() any {
		atomic.AddInt32(&created, 1)
		buf := make([]byte, 16)
		return &buf
	}}

	wcg.Add(workerNum)

	for i := 0; i < workerNum; i++ {
		go func() {
			defer wcg.Done()
			// 从pool获取一个临时对象
			buf := pool.Get()
			// pool回收一个临时对象
			defer pool.Put(buf)
		}()
	}

	wcg.Wait()
	fmt.Println("次数=", created)

}
runOut:
	次数= 3

posted @ 2024-06-03 17:39  爱编程_喵  阅读(25)  评论(0)    收藏  举报
jQuery火箭图标返回顶部代码

jQuery火箭图标返回顶部代码

滚动滑动条后,查看右下角查看效果。很炫哦!!

适用浏览器:IE8、360、FireFox、Chrome、Safari、Opera、傲游、搜狗、世界之窗.