|
အရင်ဥပမာမှာ mutex တွေကို အသုံးပြုပြီး goroutine အများကြီးကြားထဲမှာ shared state ကို access လုပ်တာကို synchronize လုပ်ခဲ့ပါတယ်။ နောက်ထပ်နည်းလမ်းတစ်ခုကတော့ goroutine တွေနဲ့ channel တွေရဲ့ built-in synchronization feature တွေကို သုံးပြီး တူညီတဲ့ရလဒ်ကို ရယူတာပါ။ ဒီ channel-based နည်းလမ်းက Go ရဲ့ “memory ကို communicating ဖြင့် မျှဝေသုံးစွဲခြင်း” နဲ့ “ဒေတာတစ်ခုချင်းစီကို တိတိကျကျ goroutine တစ်ခုက ပိုင်ဆိုင်ခြင်း” ဆိုတဲ့ အယူအဆတွေနဲ့ ကိုက်ညီပါတယ်။ |
|
![]()
package main |
|
import ( "fmt" "math/rand" "sync/atomic" "time" ) |
|
|
ဒီဥပမာမှာ ကျွန်တော်တို့ရဲ့ state ကို goroutine တစ်ခုတည်းက ပိုင်ဆိုင်မှာပါ။
ဒီနည်းက ဒေတာကို concurrent access လုပ်တဲ့အခါ ဘယ်တော့မှ ပျက်စီးမသွားစေပါဘူး။
တခြား goroutine တွေက အဲဒီ state ကို ဖတ်ချင်တာ ရေးချင်တာရှိရင်
ပိုင်ဆိုင်တဲ့ goroutine ဆီ message ပို့ပြီး အကြောင်းပြန်ချက်ကို လက်ခံရယူရပါမယ်။
ဒီ |
type readOp struct { key int resp chan int } type writeOp struct { key int val int resp chan bool } |
func main() { |
|
|
အရင်တုန်းကလိုပဲ ကျွန်တော်တို့ လုပ်ဆောင်တဲ့ operation အရေအတွက်ကို ရေတွက်ပါမယ်။ |
var readOps uint64
var writeOps uint64
|
|
|
reads := make(chan readOp)
writes := make(chan writeOp)
|
|
ဒါက |
go func() {
var state = make(map[int]int)
for {
select {
case read := <-reads:
read.resp <- state[read.key]
case write := <-writes:
state[write.key] = write.val
write.resp <- true
}
}
}()
|
|
ဒါက goroutine 100 ကို စတင်ပြီး |
for r := 0; r < 100; r++ {
go func() {
for {
read := readOp{
key: rand.Intn(5),
resp: make(chan int)}
reads <- read
<-read.resp
atomic.AddUint64(&readOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
|
|
ဒီမှာတော့ အလားတူနည်းလမ်းကိုသုံးပြီး write 10 ခုကို စတင်ပါတယ်။ |
for w := 0; w < 10; w++ {
go func() {
for {
write := writeOp{
key: rand.Intn(5),
val: rand.Intn(100),
resp: make(chan bool)}
writes <- write
<-write.resp
atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
|
|
Goroutine တွေကို တစ်စက္ကန့်လောက် အလုပ်လုပ်ခွင့်ပေးပါ။ |
time.Sleep(time.Second)
|
|
နောက်ဆုံးမှာ operation အရေအတွက်တွေကို ဖတ်ယူပြီး report လုပ်ပါ။ |
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
}
|
|
ကျွန်တော်တို့ရဲ့ ပရိုဂရမ်ကို run လိုက်တဲ့အခါ goroutine-based state management ဥပမာက စုစုပေါင်း operation 80,000 လောက် ပြီးမြောက်တာကို တွေ့ရပါတယ်။ |
$ go run stateful-goroutines.go readOps: 71708 writeOps: 7177 |
|
ဒီဥပမာအတွက်တော့ goroutine-based နည်းလမ်းက mutex-based နည်းလမ်းထက် နည်းနည်း ပိုရှုပ်ထွေးပါတယ်။ ဒါပေမယ့် တချို့ case တွေမှာတော့ အသုံးဝင်နိုင်ပါတယ်။ ဥပမာ - တခြား channel တွေပါ ပါဝင်နေတဲ့အခါ သို့မဟုတ် mutex အများကြီးကို စီမံခန့်ခွဲရတာ မှားယွင်းနိုင်ခြေများတဲ့အခါမျိုးပါ။ သင့်အနေနဲ့ ပိုပြီး သဘာဝကျတယ်လို့ ခံစားရတဲ့နည်းလမ်းကို သုံးသင့်ပါတယ်။ အထူးသဖြင့် သင့်ရဲ့ပရိုဂရမ်ရဲ့ မှန်ကန်မှုကို နားလည်ရတာနဲ့ ပတ်သက်ပြီးပါ။ |
နောက်ဥပမာ: Sorting.