人妻丰满熟妇AV无码片,岛国AV无码免费无禁网站,丰满岳乱妇一区二区三区,男插女高潮一区二区

golang 并發(fā),互斥鎖,原子操作

技術(shù)分享 2019-10-01 00:00:00
0 goroutine是否并發(fā)的問題
GoLang通過go關(guān)鍵字實現(xiàn)并發(fā)操作(真的并發(fā)嗎?),一個最簡單的并發(fā)模型:

package main

import (
"fmt"
"math/rand"
"time"
)

func routine(name string, delay time.Duration) {
t0 := time.Now()
fmt.Println(name, " start at ", t0)

// 停留xxx秒
time.Sleep(delay)

t1 := time.Now()
fmt.Println(name, " end at ", t1)

// 計算時間差
fmt.Println(name, " lasted ", t1.Sub(t0))

}

func main() {

// 生成隨機種子, 類似C語言中的srand((unsigned)time(0))生成隨機種子
rand.Seed(time.Now().Unix())

// To convert an integer number of units to a Duration, multiply
fmt.Println(time.Duration(5) * time.Second)

var name string
for i := 0; i < 3; i++ {
name = fmt.Sprintf("go_%02d", i) // 生成ID

// 生成隨機等待時間, 從0-4秒
// ntn returns, as an int, a non-negative pseudo-random number in output:mba:test gerryyang$ ./rand_t5sgo_00 start at 2013-12-28 13:25:04.460768468 +0800 HKTgo_01 start at 2013-12-28 13:25:04.460844141 +0800 HKTgo_02 start at 2013-12-28 13:25:04.460861337 +0800 HKTgo_02 end at 2013-12-28 13:25:04.460984329 +0800 HKTgo_02 lasted 122.992usgo_01 end at 2013-12-28 13:25:05.462003787 +0800 HKTgo_01 lasted 1.001159646sgo_00 end at 2013-12-28 13:25:07.461884807 +0800 HKTgo_00 lasted 3.001116339sdone*/關(guān)于goroutine是否真正并發(fā)的問題,耗子叔叔這里是這樣解釋的:
引用:
關(guān)于goroutine,我試了一下,無論是Windows還是Linux,基本上來說是用操作系統(tǒng)的線程來實現(xiàn)的。不過,goroutine有個特性,也就是說,如果一個goroutine沒有被阻塞,那么別的goroutine就不會得到執(zhí)行。這并不是真正的并發(fā),如果你要真正的并發(fā),你需要在你的main函數(shù)的第一行加上下面的這段代碼:
import runtime
runtime.GOMAXPROCS(n)”本人使用go1.2版本在Linux64,2.6.32內(nèi)核環(huán)境下測試,在上述代碼中再添加一個死循環(huán)的routine,可以驗證上述的邏輯。在沒有設(shè)置GOMAXPROCS參數(shù)時,多個goroutine會出現(xiàn)阻塞的情況;設(shè)置GOMAXPROCS參數(shù)時,下面的幾個routine可以正常執(zhí)行不會被阻塞。package main

import (
"fmt"
"math/rand"
"time"
"runtime"
)

func routine(name string, delay time.Duration) {
t0 := time.Now()
fmt.Println(name, " start at ", t0, ", sleep:", delay)

// 停留xxx秒
time.Sleep(delay)

t1 := time.Now()
fmt.Println(name, " end at ", t1)

// 計算時間差
fmt.Println(name, " lasted ", t1.Sub(t0))

}

func die_routine() {
for {
// die loop
}
}

func main() {

// 實現(xiàn)真正的并發(fā)
runtime.GOMAXPROCS(4)

fmt.Println("set runtime.GOMAXPROCS")

// 生成隨機種子, 類似C語言中的srand((unsigned)time(0))生成隨機種子
rand.Seed(time.Now().Unix())

// To convert an integer number of units to a Duration, multiply
fmt.Println(time.Duration(5) * time.Second)

// die routine
go die_routine()

var name string
for i := 0; i < 3; i++ {
name = fmt.Sprintf("go_%02d", i) // 生成ID

// 生成隨機等待時間, 從0-4秒
// ntn returns, as an int, a non-negative pseudo-random number in 這是一個經(jīng)常出現(xiàn)在教科書里賣票的例子,啟了5個goroutine來賣票,賣票的函數(shù)sell_tickets很簡單,就是隨機的sleep一下,然后對全局變量total_tickets作減一操作。
package main

import (
"fmt"
"time"
"math/rand"
"runtime"
)

var total_tickets int32 = 10

func sell_tickets(i int) {
for {
// 如果有票就賣
if total_tickets > 0 {
time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
// 賣一張票
total_tickets--
fmt.Println("id:", i, " ticket:", total_tickets)
} else {
break
}
}
}

func main() {

// 設(shè)置真正意義上的并發(fā)
runtime.GOMAXPROCS(4)

// 生成隨機種子
rand.Seed(time.Now().Unix())

// 并發(fā)5個goroutine來賣票
for i := 0; i < 5; i++ {
go sell_tickets(i)
}

// 等待線程執(zhí)行完
var input string
fmt.Scanln(&input)
// 退出時打印還有多少票
fmt.Println(total_tickets, "done")
}
/*output:id: 1 ticket: 8id: 0 ticket: 8id: 0 ticket: 7id: 2 ticket: 5id: 4 ticket: 6id: 4 ticket: 3id: 3 ticket: 3id: 1 ticket: 1id: 0 ticket: 2id: 3 ticket: -1id: 2 ticket: -1id: 1 ticket: -2id: 4 ticket: -3-3 done*/上述例子沒有考慮并發(fā)安全問題,因此需要加一把鎖以保證每個routine在售票的時候數(shù)據(jù)同步。

package main

import (
"fmt"
"time"
"math/rand"
"runtime"
"sync"
)

var total_tickets int32 = 10
var mutex = &sync.Mutex{}

func sell_tickets(i int) {

for total_tickets > 0 {

mutex.Lock()
// 如果有票就賣
if total_tickets > 0 {
time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
// 賣一張票
total_tickets--
fmt.Println("id:", i, " ticket:", total_tickets)
}
mutex.Unlock()
}
}

func main() {

// 設(shè)置真正意義上的并發(fā)
runtime.GOMAXPROCS(4)

// 生成隨機種子
rand.Seed(time.Now().Unix())

// 并發(fā)5個goroutine來賣票
for i := 0; i < 5; i++ {
go sell_tickets(i)
}

// 等待線程執(zhí)行完
var input string
fmt.Scanln(&input)
// 退出時打印還有多少票
fmt.Println(total_tickets, "done")
}
/*output:id: 0 ticket: 9id: 0 ticket: 8id: 0 ticket: 7id: 0 ticket: 6id: 0 ticket: 5id: 0 ticket: 4id: 0 ticket: 3id: 0 ticket: 2id: 0 ticket: 1id: 0 ticket: 00 done*/2 并發(fā)情況下的原子操作問題go語言也支持原子操作。關(guān)于原子操作可以參考耗子叔叔這篇文章《無鎖隊列的實現(xiàn)》,里面說到了一些CAS – CompareAndSwap的操作。下面的程序有10個goroutine,每個會對cnt變量累加20次,所以,最后的cnt應(yīng)該是200。如果沒有atomic的原子操作,那么cnt將有可能得到一個小于200的數(shù)。下面使用了atomic操作,所以是安全的。
package main

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

func main() {

var cnt uint32 = 0

// 啟動10個goroutine
for i := 0; i < 10; i++ {
go func() {
// 每個goroutine都做20次自增運算
for i := 0; i < 20; i++ {
time.Sleep(time.Millisecond)
atomic.AddUint32(&cnt, 1)
}
}()
}

// 等待2s, 等goroutine完成
time.Sleep(time.Second * 2)
// 取最終結(jié)果
cntFinal := atomic.LoadUint32(&cnt)

fmt.Println("cnt:", cntFinal)
}
/*
output:

cnt: 200
*/