一句话总结:使用锁sync.Mutex来保证数据的原子性操作、使用channel进行通信、使用select检测通信状态、使用sync.Once保证全局仅执行一次、使用runtime包设置CPU个数及时间片分配。
直接上实例
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
var g_value int = 0
var mutex sync.RWMutex
var once sync.Once
func Add(q chan<- int, value int) {
q <- value
}
func Take(q <-chan int) int {
return <-q
}
func Edit(v int) {
mutex.Lock() //原子性操作数值修改
defer mutex.Unlock()
g_value = v
fmt.Println("g_value:", g_value)
}
func initGlobal() {
fmt.Println("init global value")
}
func main() {
//CPU操作
cpuNum := runtime.NumCPU()
runtime.GOMAXPROCS(1)
fmt.Println("cpuNum:", cpuNum)
once.Do(initGlobal) //仅会执行一次initGlobal()
once.Do(initGlobal)
msgQueue := make(chan int, 10)
for i := 0; i < 10; i++ {
msgQueue <- i
}
for i := 0; i < 10; i++ {
fmt.Print(<-msgQueue, " ") //channel是先进先出队列
}
fmt.Println()
var wg sync.WaitGroup
wg.Add(5) //为何在这里就添加了?如果不加延时,可能主进程退出,还没来得及执行Add操作
for i := 0; i < 5; i++ {
go func(value int) {
runtime.Gosched() //出让一会儿CPU
defer wg.Done()
Add(msgQueue, value)
Edit(value)
}(i)
}
time.Sleep(time.Second * 1) //不加延时,select读channel的值出来可能是6,前面的go func可能还没来得及执行
//select
msgQueue <- 6
select {
case msgQueue <- 6:
fmt.Println("msgQueue<-6")
}
select {
case v := <-msgQueue:
fmt.Println(v, "<-msgQueue")
default:
fmt.Println("no msg")
}
time.Sleep(time.Second * 1)
wg.Add(5)
for i := 0; i < 5; i++ {
go func(value int) {
defer wg.Done()
Take(msgQueue)
Edit(value + 10)
}(i)
}
//time.Sleep(time.Second * 1)
wg.Wait()
}
结果:
cpuNum: 4
init global value
0 1 2 3 4 5 6 7 8 9
g_value: 0
g_value: 1
g_value: 2
g_value: 3
g_value: 4
msgQueue<-6
0 <-msgQueue
g_value: 14
g_value: 10
g_value: 11
g_value: 12
g_value: 13
成功: 进程退出代码 0.
runtime.GOMAXPROCS(4)
用4个CPU运行结果:
cpuNum: 4
init global value
0 1 2 3 4 5 6 7 8 9
g_value: 1
g_value: 2
g_value: 3
g_value: 4
g_value: 0
msgQueue<-6
1 <-msgQueue
g_value: 11
g_value: 14
g_value: 12
g_value: 10
g_value: 13
成功: 进程退出代码 0.
出让时间片后一段时间会继续运行。
package main
import (
"fmt"
"runtime"
"time"
)
func print(str string) {
runtime.Gosched()
fmt.Println(str)
}
func main() {
go print("hello")
print("boy")
time.Sleep(time.Second)
}
结果:
boy
hello
成功: 进程退出代码 0.