DEV Community

Weerasak Chongnguluam
Weerasak Chongnguluam

Posted on • Updated on

ทำความเข้าใจ sync.Mutex กับ sync.RWMutex ของ Go

ทำความเข้าใจสอง 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")
}
Enter fullscreen mode Exit fullscreen mode

จากการทำงาน 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")
}

Enter fullscreen mode Exit fullscreen mode

เราเพิ่ม 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

Buy Me A Coffee

Top comments (3)

Collapse
 
veer66 profile image
Vee Satayamas

เมื่อไหร่ทำความเข้าใจของ Rust ครับ 😛

Collapse
 
iporsut profile image
Weerasak Chongnguluam

น่าสนใจครับ ว่าจะเขียนเรื่อง mpsc เพราะชอบลืมว่ามันย่อมากจาก multiple producer single consumer

Collapse
 
veer66 profile image
Vee Satayamas

ข้ามไป mpmc ใน crossbeam เลยดีไหมครับ 😅