Mutex bir vaqtning o'zida ko'pi bilan bitta goroutine kirishiga ruxsat beradi. Ammo ba'zan tizim resurslari (masalan, CPU yadrolari soni, ma'lumotlar bazasi pool'i) bir vaqtning o'zida ko'pi bilan $N$ ta ($N > 1$) goroutine ishlashiga imkon beradi. Buning uchun Semafor qo'llaniladi.
| Xususiyat | Mutex | Semafor (Semaphore) |
|---|---|---|
| Ruxsat berilgan oqimlar | Aniq 1 ta | Ko'pi bilan $N$ ta |
| Asosiy vazifasi | Ma'lumotlarni poygadan himoyalash | Resurs yuklamasini cheklash (rate limit) |
| Go dagi tayyor shakli | sync.Mutex | make(chan struct{}, N) yoki x/sync/semaphore |
Go tilida buferlangan kanal yordamida qulay semafor strukturasini qurish mumkin:
type Semaphore struct {
tokens chan struct{}
}
func NewSemaphore(limit int) *Semaphore {
return &Semaphore{
tokens: make(chan struct{}, limit),
}
}
func (s *Semaphore) Acquire() {
s.tokens <- struct{}{} // agar bufer to'lsa, bloklanadi
}
func (s *Semaphore) Release() {
<-s.tokens // tokenni bo'shatadi
}
Rendezvous (Uchrashuv nuqtasi): Ikkita mustaqil goroutine ma'lum bir nuqtaga kelgandagina bir-birini kutib, so'ng birgalikda oldinga harakatlanishi kerak bo'lgan holat:
func main() {
arrivedA := make(chan struct{})
arrivedB := make(chan struct{})
go func() {
fmt.Println("Goroutine A tayyor")
close(arrivedA) // men yetib keldim
<-arrivedB // B ni kutaman
fmt.Println("Goroutine A birga boshladi!")
}()
go func() {
fmt.Println("Goroutine B tayyor")
close(arrivedB) // men yetib keldim
<-arrivedA // A ni kutaman
fmt.Println("Goroutine B birga boshladi!")
}()
time.Sleep(100 * time.Millisecond)
}
Semaphore strukturasini yozing va unga Acquire() hamda Release() metodlarini qo'shing.
package main
import (
"fmt"
"sync"
"time"
)
type Semaphore struct {
// TODO: buferlangan kanal maydonini e'lon qiling
}
func NewSemaphore(n int) *Semaphore {
// TODO: n ta sig'imli semafor qaytaring
return nil
}
func (s *Semaphore) Acquire() {
// TODO: token oling
}
func (s *Semaphore) Release() {
// TODO: tokenni bo'shating
}
func main() {
sem := NewSemaphore(2)
var wg sync.WaitGroup
for i := 1; i <= 4; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
sem.Acquire()
defer sem.Release()
fmt.Printf("Vazifa #%d boshlandi
", id)
time.Sleep(50 * time.Millisecond)
fmt.Printf("Vazifa #%d yakunlandi
", id)
}(i)
}
wg.Wait()
}
Semaforlar bir vaqtning o'zida parallel bajarilayotgan amallar sonini qat'iy nazoratda ushlab turish uchun tengsiz vositadir.
Keyingi mavzu: Signaling (Signallar, sync.Once va sync.Pool)!