ในทางเทคนิค โค้ดนี้ทำงานได้ถูกต้อง (Thread-safe) แต่ในเชิงปฏิบัติมี 3 เหตุผลหลัก ที่ไม่แนะนำครับ:
ตัวอย่างที่แนะนำ:
mu.Lock()defer mu.Unlock() // ปลอดภัยกว่า
s.val++
อยากให้ช่วยดูวิธีปรับเป็นแบบ atomic เพื่อเพิ่มความเร็วด้วยไหมครับ?
การใช้ Closure ร่วมกับ Mutex มักใช้เพื่อสร้างสิ่งที่เรียกว่า "Thread-safe Wrapper" ครับ วิธีนี้จะช่วยซ่อนความซับซ้อนของการ Lock ไว้ข้างใน ทำให้คนเรียกใช้ (Caller) ไม่ต้องกังวลเรื่องการลืม Unlock
นี่คือ Pattern ที่นิยมใช้กัน:
func (s *State) Update(fn func(current int) int) {
s.mu.Lock()
defer s.mu.Unlock()
// ส่งค่าปัจจุบันเข้าไปใน Closure และรับค่าใหม่กลับมา
s.val = fn(s.val)
}
// วิธีใช้งาน
state.Update(func(current int) int {
return current + 1 // เขียน logic อะไรตรงนี้ก็ได้ จะคูณ จะหาร หรือเช็ค if
})
func (s *Store) WithLock(action func(s *Store)) {
s.mu.Lock()
defer s.mu.Unlock()
action(s)
}
// วิธีใช้งาน: ทุกอย่างในนี้จะถูก Lock ไว้ด้วยกัน
store.WithLock(func(s *Store) {
s.val++
s.lastUpdated = time.Now()
fmt.Println("Updated safe!")
})
ทำไมถึงน่าใช้?
ข้อควรระวัง: อย่าเรียกฟังก์ชันอื่นที่พยายามจะ Lock Mutex ตัวเดิมซ้ำซ้อนภายใน Closure เพราะจะเกิด Recursive Deadlock ได้ครับ
สนใจอยากลองดูตัวอย่างการใช้ RWMutex เพื่อแยกการอ่านและการเขียนให้เร็วขึ้นไหมครับ?
การใช้ sync.RWMutex จะช่วยให้โปรแกรมทำงานเร็วขึ้นมากในกรณีที่มีการ "อ่านเยอะ แต่เขียนน้อย" ครับ
ความต่างคือ:
ตัวอย่างการใช้ RWMutex Pattern
type Stats struct {
mu sync.RWMutex
counters map[string]int
}
// 1. Write Lock: ใช้ Closure เพื่ออัปเดตค่า (ล็อกทุกคน)
func (s *Stats) Update(name string, fn func(current int) int) {
s.mu.Lock()
defer s.mu.Unlock()
s.counters[name] = fn(s.counters[name])
}
// 2. Read Lock: ใช้ Closure เพื่ออ่านค่า (อ่านพร้อมกันได้ ไม่ช้า)
func (s *Stats) Snapshot(fn func(data map[string]int)) {
s.mu.RLock()
defer s.mu.RUnlock()
// ส่งข้อมูลออกไปให้ประมวลผล (เช่น Print หรือส่ง API) แบบ Read-only
fn(s.counters)
}
ทำไมแบบนี้ถึง "เทพ" กว่า?
ข้อควรระวัง: ภายใน RLock ห้ามเขียนข้อมูลเด็ดขาด และอย่าทำ Logic ที่ใช้เวลานานเกินไป เพราะจะไปขวางคนที่รอ Lock (Write) อยู่ครับ
อยากให้ลองทำตัวอย่าง Benchmark เปรียบเทียบความเร็วระหว่าง Mutex ปกติกับ RWMutex ดูไหมครับ?