|
Trong ví dụ trước chúng ta sử dụng explicit locking với mutexes để đồng bộ hóa truy cập chia sẻ trạng thái giữa nhiều goroutine. Một lựa chọn khác là sử dụng tính năng đồng bộ hóa built-in của goroutine và channel để đạt được kết quả tương tự. Hướng tiếp cận dựa trên channel này phù hợp với ý tưởng chia sẻ bộ nhớ của Go bằng cách giao tiếp và sở hữu từng phần dữ liệu bởi chính xác một goroutine. |
|
![]()
package main |
|
import ( "fmt" "math/rand" "sync/atomic" "time" ) |
|
|
Trong ví dụ này trạng thái sẽ được sử hữu bởi một
goroutine duy nhất. Điều này sẽ đảm bảo rằng dữ liệu
sẻ không bao giờ bị hỏng hoặc truy cập đồng thời. Để
đọc hoặc khi trạng thái đó, những goroutine khác sẽ
gửi thông tin đến goroutine đang sử hữu và nhận trả
lời tương ứng. Những |
type readOp struct { key int resp chan int } type writeOp struct { key int val int resp chan bool } |
func main() { |
|
|
Như trước đó, chúng ta sẽ đếm số lượng toán tử được thực thi. |
var readOps uint64
var writeOps uint64
|
|
Các channel |
reads := make(chan readOp)
writes := make(chan writeOp)
|
|
Đây là goroutine mà chiếm quyền |
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
}
}
}()
|
|
Chạy 100 goroutine để thực hiện tháo tác reads đến
goroutine sở hữu trạng thái qua channel |
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)
}
}()
}
|
|
Chúng ta cho chạy 10 toán tử writes, sử dụng cách tiếp cận tương tự. |
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 hoạt động trong một giây. |
time.Sleep(time.Second)
|
|
Cuối cùng, capture và báo cáo số lượng toán tử. |
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
}
|
|
Running our program shows that the goroutine-based state management example completes about 80,000 total operations. Khi chạy chương trình chỉ ra rằng ví dụ của sự quản lý trạng thái dựa trên goroutine hoàn thành tổng cộng khoảng 80,000 phép tính. |
$ go run stateful-goroutines.go readOps: 71708 writeOps: 7177 |
|
Đối với trường hợp cụ thể này, hướng tiếp cận dựa trên goroutine liên quan nhiều hơn đến cái dựa trên mutex. Nó có thể hữu ích trong một số trường hợp cụ thể, ví dụ khi bạn có nhiều channel tham gia hoặc khi quản lý nhiều mutex như vậy sẽ gây lỗi. Bạn nên sử dụng bất kì phương pháp nào cảm thấy tự nhiên nhất, đặc biệt là đối với sự hiểu biết đúng đắn của chương trình. |
Ví dụ tiếp theo: Sorting.