Skip to content

Mutex

Mutex ko'plab goroutinelar bir vaqtning o'zida umumiy (shared) xotira manziliga kirishini boshqarish uchun ishlatiladigan vosita hisoblanadi. U sync paketida joylashgan bo'lib, parallel ishlovchi jarayonlar o'rtasida race condition ni oldini olishda muhim rol o'ynaydi.

Tasavvur qiling, sizda bank hisob raqami mavjud. Ayni bir vaqtda quyidagi ikkita operatsiyani bajarmoqchisiz:

  • Bankomat orqali pul yechmoqchisiz
  • Mobil ilova orqali boshqa kartaga pul o'tkazmoqchisiz

Bu ikkala operatsiya bir vaqtda bajarilsa, har bir operatisya bajarilishidan oldin hisobni tekshiradi va bir xil balans masalan 1000 so'm degan natijani oladi. Natijada:

  • Bankomat 1000 so'm yechadi
  • Mobil ilova ham 1000 so'm o'tkazadi
  • Jami 2000 so'm sarflanadi, lekin hisobda faqat 1000 so'm bor edi!

Bu holat race condition deb ataladi. Ya'ni, bir nechta goroutinelar bir vaqtda bir xil ma'lumot bilan ishlayotgani uchun noto'g'ri natija yuzaga keladi.

Bunday muammoni oldini olish uchun mutexdan foydalaniladi. Ya'ni, har bir operatsiya boshlanishidan avval mutex.Lock() orqali resursga kirishni qulflaydi, va ish tugagach mutex.Unlock() orqali qulfni bo'shatadi. Shu orqali bir vaqtda faqat bitta operatsiya hisob raqam bilan ishlashi mumkin bo'ladi.

Yuqoridagi bank misolni kichik dastur yozib yaxshiroq tushunishga harakat qilamiz.

Bu misolda:

  • Bitta bank hisob raqami mavjud (boshlang'ich balans: 1000 so'm)
  • Ikki goroutine:
    • biri bankomatdan pul yechadi
    • biri mobil ilovadan pul o'tkazadi
  • Har bir operatsiya hisobni tekshiradi va pulni ayiradi.

Mutexsiz misol:

package main

import (
    "fmt"
    "sync"
    "time"
)

var balance = 1000

func withdraw(name string, amount int, wg *sync.WaitGroup) {
    defer wg.Done()

    fmt.Printf("%s Pul yechmoqchi: %d so'm\n", name, amount)

    currentBalance := balance
    fmt.Printf("%s O'qilgan balans: %d\n", name, currentBalance)

    time.Sleep(time.Millisecond * 500)

    if currentBalance >= amount {
        fmt.Printf("%s Shart bajardi: balans >= %d\n", name, amount)
        balance = currentBalance - amount
        fmt.Printf("%s Pul yechildi! Yangi balans: %d\n", name, balance)
    } else {
        fmt.Printf("%s Yetarli mablag' yo'q! Balans: %d\n", name, currentBalance)
    }
}

func main() {
    var wg sync.WaitGroup

    wg.Add(2)
    go withdraw("Bankomat", 800, &wg)
    go withdraw("Mobil ilova", 700, &wg)

    wg.Wait()
    fmt.Println("Operatsiyalar tugadi. Yakuniy balans:", balance)
}

Natija:

Mobil ilova Pul yechmoqchi: 700 so'm
Mobil ilova O'qilgan balans: 1000
Bankomat Pul yechmoqchi: 800 so'm
Bankomat O'qilgan balans: 1000
Bankomat Shart bajardi: balans >= 800
Bankomat Pul yechildi! Yangi balans: 200
Mobil ilova Shart bajardi: balans >= 700
Mobil ilova Pul yechildi! Yangi balans: 300
Operatsiyalar tugadi. Yakuniy balans: 300

Nima sodir bo'lid?

  • Hisobda 1000 so'm bor.
  • Ikkita operatsiya bir vaqtda bo'lmoqda:
    • Bankomat: 800 so'm yechmoqchi
    • Mobil ilova: 700 so'm yechmoqchi

Agar operatsiyalar navbat bilan, xavfsiz tarzda bajarilganida:

  1. Bankomat 800 so'mni oladi -> Qolgan: 200
  2. Mobil ilova tekshiradi: 700 > 200 -> mablag' yetmaydi -> pul yechilmasligi kerak edi.

Mobil ilova pulni yechmasligi kerak edi, lekin race condition bo'lganida u yolg'on shartni bajarib yuboradi, chunki mobil ilova balans o'zgarishini bilmaydi.


Mutex bilan misol:

package main

import (
    "fmt"
    "sync"
    "time"
)

var balance = 1000
var mu sync.Mutex

func withdraw(name string, amount int, wg *sync.WaitGroup) {
    defer wg.Done()

    mu.Lock() // shu yerda qulflanadi
    defer mu.Unlock() // funksiya ishini yakunlagach ochiladi.

    fmt.Printf("%s Pul yechmoqchi: %d so'm\n", name, amount)

    currentBalance := balance
    fmt.Printf("%s O'qilgan balans: %d\n", name, currentBalance)

    time.Sleep(time.Millisecond * 500)

    if currentBalance >= amount {
        fmt.Printf("%s Shart bajardi: balans >= %d\n", name, amount)
        balance = currentBalance - amount
        fmt.Printf("%s Pul yechildi! Yangi balans: %d\n", name, balance)
    } else {
        fmt.Printf("%s Yetarli mablag' yo'q! Balans: %d\n", name, currentBalance)
    }
}

func main() {
    var wg sync.WaitGroup

    wg.Add(2)
    go withdraw("Bankomat", 800, &wg)
    go withdraw("Mobil ilova", 700, &wg)

    wg.Wait()
    fmt.Println("Operatsiyalar tugadi. Yakuniy balans:", balance)
}

Natija:

Bankomat Pul yechmoqchi: 800 so'm
Bankomat O'qilgan balans: 1000
Bankomat Shart bajardi: balans >= 800
Bankomat Pul yechildi! Yangi balans: 200
Mobil ilova Pul yechmoqchi: 700 so'm
Mobil ilova O'qilgan balans: 200
Mobil ilova Yetarli mablag' yo'q! Balans: 200
Operatsiyalar tugadi. Yakuniy balans: 200

Endi nima bo'ldi?

  • mutex orqali har bir goroutine navbat bilan ishlayapti.
  • Har biri balance o'zgaruvchisini o'zi yolg'iz o'qiydi va o'zgartiradi shu bilan race conditionni butunlay oldi olinadi.