|
V předchozím příkladu jsme použili explicitní zamykání s mutexy pro synchronizaci přístupu ke sdílenému stavu přes několik gorutin. Jinou možností k dosažení toho stejného výsledku je použít zabudované vlastnosti pro synchronizaci goroutin a kanálů. TEnto přístup založený na kanálech odpovídá myšlenkám Go se sdílením paměti pomocí komunikace, kdy každý kousek dat je vlastněný přesně 1 goroutinou. |
|
package main
|
|
import (
"fmt"
"math/rand"
"sync/atomic"
"time"
)
|
|
|
V tomto příkladu bude náš stav vlastněný jednou
gorutinou. To zajistí že data nebudou nikdy během souběžného
přístupu poškozena. K přečtení nebo zápisu tohoto stavu
ostatní gorutiny pošlou zprávu vlastnící gorutině
a obdrží odpovídající odpovědi.
Tyto struktury |
type readOp struct {
key int
resp chan int
}
type writeOp struct {
key int
val int
resp chan bool
}
|
func main() {
|
|
|
Stejně jako předtím budeme počítat kolik operací jsme udělali. |
var readOps uint64
var writeOps uint64
|
|
Kanály |
reads := make(chan readOp)
writes := make(chan writeOp)
|
|
Zde je gorutina která vlastní |
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
}
}
}()
|
|
Toto nastaruje 100 gorutin pro vyvolání čtení od
stav vlastnící gorutiny skrze kanál |
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)
}
}()
}
|
|
A také nastartujeme 10 zápisů s použitím stejného přístupu. |
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)
}
}()
}
|
|
Nechá gorutiny vteřinu pracovat. |
time.Sleep(time.Second)
|
|
Na závěr načti a vypiš počty operací. |
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
}
|
|
Spuštění našeho programu ukazuje že správa stavu založená na gorutině provedla celkem přibližně 80,000 operací. |
$ go run stateful-goroutines.go
readOps: 71708
writeOps: 7177
|
|
V tomto konkrétním případě byl přístup založený na gorutinách trochu komplikovanější než přístup založený na mutexu. Přesto ale může být v některých případech užitečný, například když jsou zapojeny i další kanály, nebo když by správa více takových mutexů byla náchylná k chybám. Měl/a bys zvolit takový přístup který se zdá přirozenější, obzvlášť s respektem k pochopení správnosti tvého programu. |
Další příklad: Třídění.