10
votes

Acquérir une serrure sur deux mutiles et éviter l'impasse

Le code suivant contient une impasse potentielle, mais semble nécessaire: Pour copier en toute sécurité les données sur un conteneur d'un autre, les deux conteneurs doivent être verrouillés pour empêcher les modifications de se produire dans un autre thread.

void foo::copy(const foo & rhs)
{
    pMutex->lock();
    rhs.pMutex->lock();
    // do copy
}


1 commentaires

STD :: LOCK A un algorithme d'évitement Deadlock, passe-t-il les mautexes et il sera plus lisible aux autres que de la mise en œuvre de votre choix.


6 Réponses :


16
votes

imposer une sorte d'ordre total sur les instances de foo et d'acquérir toujours leurs serrures dans l'ordre croissant ou décroissant, par exemple, par exemple , FOO1-> verrouillage () puis foo2-> verrouillage () .

Une autre approche consiste à utiliser la sémantique fonctionnelle et à écrire un FOO :: clone méthode crée une nouvelle instance plutôt que de clouer un existant.

Si votre code fait beaucoup de verrouillage, vous aurez peut-être besoin d'un algorithme d'évitement de blocage complexe tel que le L'algorithme du banquier .


3 commentaires

Même quelque chose d'aussi simple que les adresses de ce VS RHS travailleraient. Toujours verrouiller celui-ci avec l'adresse inférieure en premier.


Le clone ne fonctionnerait bien que s'il ne copie pas, et je ne pense pas que le partage implicite fonctionnera, mais je vais jeter un coup d'œil. Approche intéressante Kyle. Je ne peux voir aucun défaut.


C'est une bonne solution pour lui suggérer de faire une copie temporaire des données. std :: verrouillage offre déjà un tel algorithme d'évitement de blocage. et serait plus lisible



1
votes

Que diriez-vous de cela?

void foo::copy(const foo & rhs)
{
    scopedLock lock(rhs.pMutex); // release mutex in destructor
    foo tmp(rhs);
    swap(tmp); // no throw swap locked internally
}


0 commentaires

-1
votes

Pour éviter une impasse, c'est probablement le meilleur, d'attendre que les deux ressources puissent être verrouillées:

Ne sais pas quelle API mutex que vous utilisez, donc ici est un pseudo pseudo arbitraire, supposons que Can_Lock () Code > Seulement des chèques si cela peut verrouiller un mutex, et que try_lock () code> renvoie true si elle était verrouillée et false, si le mutex est déjà verrouillé par quelqu'un d'autre. P>

void foo::copy(const foo & rhs)
{
    for(;;)
    {
        if(! pMutex->cany_lock() || ! rhs.pMutex->cany_lock())
        {
            // Depending on your environment call or dont call sleep()
            continue;
        }
        if(! pMutex->try_lock())
            continue;
        if(! rhs.pMutex->try_lock())
        {
            pMutex->try_lock()
            continue;
        }
        break;
    }
    // do copy
}


1 commentaires

Pour éviter une impasse, il est préférable d'introduire un Livelock? Et tourner, en utilisant 100% cpu?



-2
votes

Vous pouvez essayer de verrouiller les mautexes simultanément à l'aide de SCOPED_LOCK ou Auto_Lock .... comme le transfert bancaire do ...

void Transfer(Receiver recv, Sender send)
{
    scoped_lock rlock(recv.mutex);
    scoper_lock slock(send.mutex);

    //do transaction.
}


1 commentaires

Ceci est une recette pour le désastre



0
votes

Ceci est un problème connu déjà il y a une solution STD. STD :: LOCK () peut être appelé sur 2 ou plusieurs mutex en même temps tout en évitant l'impasse. Plus d'informations ici Il offre une recommandation.

std :: scoped_lock propose une enveloppe Raii pour cette fonction et est Généralement préféré à un appel nu à STD :: Verrouiller.

Bien sûr, cela n'autorise pas vraiment les premières sorties d'un verrouillage au-dessus de l'autre alors utilisez std :: défer_lock ou std :: adopt_lock comme je l'ai fait dans cette < Un href = "https://stackoverflow.com/a/54713794/1943599"> répondez à une question similaire.


0 commentaires

1
votes

Comme @Melleter mentionné, vous pouvez utiliser std :: verrouillage pour verrouillage plusieurs mutiles en évitant l'impasse. xxx

mais note pour vérifier que rhs n'est pas un * Ceci car dans ce cas std :: verrouillage conduira à UB due au même mordex.


1 commentaires

Toute raison pour laquelle vous ne feriez pas std :: unique_lock d'abord avec std :: defer_lock puis effectuez std :: verrouillage (verrouillage) , L2); ? Ou est-ce juste une préférence personnelle / style?