ทำความเข้าใจสอง type ที่ช่วยในการ lock การแย่งกันใช้ resources
จริงๆ Mutex นี่มันเป็นการผสมมาจากคำว่า Mutual Exclusion พอมาอยู่ในบริบทของการ Lock ก็คือกันไม่ให้เกิดการแย่งกันทำงาน ในบริบทของสอง type นี้ของ Go นั่นก็คือ กันไม่ให้หลายๆ goroutines แย่งการทำงานหรือแย่งกันใช้ resources ที่เราต้องการจำกัดการเข้าถึงของหลายๆ goroutine นั่นเอง
type sync.Mutex
เริ่มที่ type sync.Mutex
นั้นช่วยให้เรา lock แบบ ณ ขณะเวลาใดนั้นจะมีแค่ 1 goroutine ที่ใช้งานได้จนกว่ามันจะ unlock ส่วน goroutine อื่นๆที่ขอ lock การใช้งานเมื่อมี goroutine อื่น lock ไว้ก็จะต้องหยุดรออยู่อย่างนั้น
ตัวอย่างเช่นโค้ดต่อไปนี้
package main
import (
"fmt"
"sync"
"time"
)
var m sync.Mutex
var n = 10
func p() {
m.Lock()
fmt.Println(n)
time.Sleep(1 * time.Second)
m.Unlock()
}
func main() {
go p()
p()
time.Sleep(3 * time.Second)
fmt.Println("DONE")
}
จากการทำงาน function p
เราจะใช้ m ที่เป็น sync.Mutex
โดยเรียก method Lock
เพื่อกัน goroutine อื่นรันโค้ดต่อไปจากนี้จนกว่าจะเรียก method Unlock
ทำให้ถ้าเราเรียกแบบใน main ที่แยกรันอีก goroutine ทั้งสอง goroutine นี้จะ block กัน มันจะรัน go p()
จนกว่าจะเรียก m.Unlock()
การทำงานของ p()
ถึงจะไปต่อจากบรรทัดที่เรียก m.Lock()
ได้
type sync.RWMutex
ส่วน type sync.RWMutex
นั้นมี Lock()
กับ Unlock()
เหมือนกัน แต่จะเพิ่มกลไก RLock()
กับ RUnlock()
เข้ามา เพื่อขอ lock สำหรับอ่านอย่างเดียว ซึ่งจะทำให้ทำให้คนที่ขอ RLock()
เหมือนกันทำงานพร้อมกันได้ แต่จะ lock คนที่ขอ Lock()
เพื่อแก้ไขข้อมูล
ตัวอย่างโค้ดเช่น
package main
import (
"fmt"
"sync"
"time"
)
var rw sync.RWMutex
var n = 10
func p() {
rw.RLock()
fmt.Println(n)
time.Sleep(1 * time.Second)
rw.RUnlock()
}
func q(v int) {
rw.Lock()
n = v
time.Sleep(1 * time.Second)
rw.Unlock()
}
func main() {
go p()
p()
q(20)
time.Sleep(3 * time.Second)
fmt.Println("DONE")
}
เราเพิ่ม q
เข้ามาเพื่อเป็นฟังก์ชันที่เขียนข้อมูลโดยเรียก Lock()
ถ้าเกิด q
สามารถ lock ได้ goroutine อื่นๆที่เรียก rw.Lock()
หรือ rw.RLock()
ต้องรอ goroutine ที่ได้การทำงานไป Unlock()
ก่อน
ถ้ามี goroutine ทีทำงาน p
ที่เรียก RLock()
เอาไว้ก่อน แล้วมี goroutine อื่นที่รัน p
เหมือนกันด้วยจะไม่ block กันเพราะถ้ามี RLock()
ทำงานอยู่มันจะ block เฉพาะ Lock()
เท่านั้น
การ block จะเป็นตามตารางนี้
RLock | Lock | |
---|---|---|
RLock | NOT BLOCK | BLOCK |
Lock | BLOCK | BLOCK |
Top comments (3)
เมื่อไหร่ทำความเข้าใจของ Rust ครับ 😛
น่าสนใจครับ ว่าจะเขียนเรื่อง mpsc เพราะชอบลืมว่ามันย่อมากจาก multiple producer single consumer
ข้ามไป mpmc ใน crossbeam เลยดีไหมครับ 😅