68 / 73

Semaphores

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.

Bob mundarijasi:


Mutex va Semafor taqqoslanishi

XususiyatMutexSemafor (Semaphore)
Ruxsat berilgan oqimlarAniq 1 taKo'pi bilan $N$ ta
Asosiy vazifasiMa'lumotlarni poygadan himoyalashResurs yuklamasini cheklash (rate limit)
Go dagi tayyor shaklisync.Mutexmake(chan struct{}, N) yoki x/sync/semaphore

Kanal yordamida Semafor yaratish

Go tilida buferlangan kanal yordamida qulay semafor strukturasini qurish mumkin:

example.go
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 andozasi

Rendezvous (Uchrashuv nuqtasi): Ikkita mustaqil goroutine ma'lum bir nuqtaga kelgandagina bir-birini kutib, so'ng birgalikda oldinga harakatlanishi kerak bo'lgan holat:

example.go
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)
}

Topshiriq: Semafor struct'ini yaratish

Semaphore strukturasini yozing va unga Acquire() hamda Release() metodlarini qo'shing.

example.go
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()
}

Xulosa va keyingi qadamlar

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)!