sync.Mutex.TryLock în Go

Încearcă să încuie mutexul fără să aștepte: returnează true dacă a reușit, false dacă lacătul este ocupat.

Sintaxă
func (m *Mutex) TryLock() bool
func (rw *RWMutex) TryLock() bool
func (rw *RWMutex) TryRLock() bool

Ce face sync.Mutex.TryLock

mu.TryLock() încuie mutexul și returnează true dacă era liber. Dacă alt goroutine îl ține, nu așteaptă, ci returnează imediat false. Când a returnat true, trebuie să apelezi Unlock, ca după Lock.

La sync.RWMutex, TryLock încearcă blocarea pentru scriere, iar TryRLock pe cea pentru citire (reușește dacă nu scrie nimeni).

Documentația Go avertizează că folosirile corecte sunt rare: de obicei e mai bine să aștepți cu Lock. Un caz potrivit este o sarcină pe care o sari dacă rulează deja, ca în al doilea exemplu.

Ce returnează

true dacă lacătul a fost obținut, false dacă era ocupat.

Exemple

Exemplul 1: Liber sau ocupat

package main

import (
	"fmt"
	"sync"
)

func main() {
	var mu sync.Mutex
	mu.Lock()
	fmt.Println("TryLock cât e ocupat:", mu.TryLock())
	mu.Unlock()

	if mu.TryLock() {
		fmt.Println("TryLock după Unlock: true")
		mu.Unlock()
	}

	var rw sync.RWMutex
	rw.RLock()
	fmt.Println("TryRLock cu un cititor activ:", rw.TryRLock())
	fmt.Println("TryLock cu cititori activi: ", rw.TryLock())
	rw.RUnlock()
	rw.RUnlock()
	fmt.Println("TryLock după RUnlock:       ", rw.TryLock())
	rw.Unlock()
}

Rezultat

TryLock cât e ocupat: false
TryLock după Unlock: true
TryRLock cu un cititor activ: true
TryLock cu cititori activi:  false
TryLock după RUnlock:        true

Exemplul 2: Sari peste o sarcină care rulează deja

Raportul se generează o singură dată; cererile care vin cât timp rulează nu așteaptă, ci primesc un mesaj.

package main

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

var generare sync.Mutex

func genereazaRaport() string {
	if !generare.TryLock() {
		return "raportul se generează deja, revino mai târziu"
	}
	defer generare.Unlock()
	time.Sleep(300 * time.Millisecond)
	return "raport generat"
}

func main() {
	rezultate := make([]string, 2)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		rezultate[0] = genereazaRaport()
	}()
	time.Sleep(50 * time.Millisecond) // a doua cerere vine în timpul primei
	rezultate[1] = genereazaRaport()
	wg.Wait()
	fmt.Println("Cererea 1:", rezultate[0])
	fmt.Println("Cererea 2:", rezultate[1])
	fmt.Println("Cererea 3:", genereazaRaport())
}

Rezultat

Cererea 1: raport generat
Cererea 2: raportul se generează deja, revino mai târziu
Cererea 3: raport generat

Greșeli frecvente

  • Faci o buclă for !mu.TryLock() {} ca să „aștepți activ”. Consumă procesorul degeaba; folosește Lock().
  • Apelezi Unlock și când TryLock a returnat false. Descuierea unui mutex pe care nu îl ții oprește programul.

Comenzi înrudite

  • sync.Mutex: Lacăt de excludere reciprocă: doar un goroutine pe rând poate rula codul dintre Lock() și Unlock().
  • sync.RWMutex: Lacăt pentru citire și scriere: mai mulți cititori pot lucra deodată (RLock), dar un scriitor lucrează singur (Lock).