3
votes

Vous essayez d'acquérir une serrure avec une date limite en golang?

Comment peut-on seulement essayer d'acquérir un verrou de type mutex en cours de route, soit en abandonnant immédiatement (comme le fait TryLock dans d'autres implémentations), soit en respectant une forme de délai (essentiellement LockBefore )?

Je peux penser à 2 situations en ce moment où cela serait très utile et où je cherche une sorte de solution. Le premier est: un service lourd en CPU qui reçoit des requêtes sensibles à la latence (par exemple, un service Web). Dans ce cas, vous voudrez faire quelque chose comme l'exemple RPCService ci-dessous. Il est possible de l'implémenter en tant que file d'attente de travail (avec des canaux et des éléments), mais dans ce cas, il devient plus difficile d'évaluer et d'utiliser tout le processeur disponible. Il est également possible d'accepter simplement qu'au moment où vous acquérez le verrou, votre code est peut-être déjà dépassé, mais ce n'est pas idéal car cela gaspille une certaine quantité de ressources et signifie que nous ne pouvons pas faire des choses comme un "ad-hoc dégradé response ".

    /* Example 2: TryLock() for updating stats. */
    func (s *StatsObject) updateObjStats(key, value interface{}) {
      if s.someObj[key].TryLock() {
        defer s.someObj[key].Unlock()
        ... update stats ...
        ... fill in s.cheapCachedResponse ...
      }
    }

    func (s *StatsObject) UpdateStats() {
      s.someObj.Range(s.updateObjStats)
    }

Un autre cas est celui où vous avez un groupe d'objets qui devraient être touchés, mais qui peuvent être verrouillés, et où les toucher devrait se terminer dans un certain délai temps (par exemple, mettre à jour certaines statistiques). Dans ce cas, vous pouvez également utiliser LockBefore () ou une forme de TryLock () , voir l'exemple de statistiques ci-dessous.

    /* Example 1: LockBefore() for latency sensitive code. */
    func (s *RPCService) DoTheThing(ctx context.Context, ...) ... {
      if s.someObj[req.Parameter].mtx.LockBefore(ctx.Deadline()) {
        defer s.someObj[req.Parameter].mtx.Unlock()
        ... expensive computation based on internal state ...
      } else {
        return s.cheapCachedResponse[req.Parameter]
      }
    }


3 Réponses :


3
votes

Je pense que vous posez plusieurs questions ici:

  1. Cette fonction existe-t-elle dans la bibliothèque standard? Non, ce n'est pas le cas. Vous pouvez probablement trouver des implémentations ailleurs - il est possible d'implémenter en utilisant la bibliothèque standard (atomics, par exemple).

  2. Pourquoi cette fonctionnalité n'existe-t-elle pas dans la bibliothèque standard: le problème que vous avez mentionné dans la question est une discussion. Plusieurs discussions sur la liste de diffusion go-fous ont également lieu avec la contribution de plusieurs développeurs de code Go: lien 1 , lien 2 . Et il est facile de trouver d'autres discussions sur Google.

  3. Comment puis-je concevoir mon programme de manière à ce que je n'en ai pas besoin?

La réponse à (3) est plus nuancée et dépend de votre problème exact. Votre question dit déjà

Il est possible de l'implémenter en tant que file d'attente de travail (avec des canaux et trucs), mais dans ce cas, il devient plus difficile de jauger et utiliser tous les processeurs disponibles

Sans fournir de détails sur les raisons pour lesquelles il serait plus difficile d'utiliser tous les processeurs, par opposition à la vérification d'un état de verrouillage mutex.

Dans Go, vous voulez généralement des canaux chaque fois que les schémas de verrouillage deviennent non triviaux. Cela ne devrait pas être plus lent et il devrait être beaucoup plus facile à maintenir.


0 commentaires

19
votes

Utilisez un canal avec une taille de tampon de un comme mutex.

select {
case l <- struct{}{}:
    // lock acquired
    <-l
case <-time.After(time.Minute):
    // lock not acquired
}

Verrouiller:

select {
case l <- struct{}{}:
    // lock acquired
    <-l
default:
    // lock not acquired
}

Déverrouiller:

<-l

Essayez le verrouillage:

l <- struct{}{}

Essayez avec timeout:

l := make(chan struct{}, 1)


0 commentaires

0
votes

Que diriez-vous de ce package: https://github.com/viney-shih/go- verrouiller . Il utilise canal et sémaphore ( golang.org/x/sync/semaphore ) pour résoudre votre problème.

go-lock implémente TryLock , TryLockWithTimeout et TryLockWithContext fonctions en plus de Lock et Unlock. Il offre une flexibilité pour contrôler les ressources.

Exemples:

package main

import (
    "fmt"
    "time"
    "context"

    lock "github.com/viney-shih/go-lock"
)

func main() {
    casMut := lock.NewCASMutex()

    casMut.Lock()
    defer casMut.Unlock()

    // TryLock without blocking
    fmt.Println("Return", casMut.TryLock()) // Return false

    // TryLockWithTimeout without blocking
    fmt.Println("Return", casMut.TryLockWithTimeout(50*time.Millisecond)) // Return false

    // TryLockWithContext without blocking
    ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
    defer cancel()

    fmt.Println("Return", casMut.TryLockWithContext(ctx)) // Return false


    // Output:
    // Return false
    // Return false
    // Return false
}


0 commentaires