J'ai rencontré une question dans Leetcode . J'ai consulté des solutions dans le Discutez . Mais mon Solution est différent des autres parce que je n'utilise pas Le verrouillage Je pense qu'il n'est pas nécessaire d'utiliser code> dans la méthode
d'abord code>. Je me demande si mon code est correct. En outre, pouvez-vous me donner des conseils sur mon code?
unique_lock code> dans la méthode
vide d'abord (fonction
void deuxième (fonction
class Foo {
public:
Foo() {
}
void first(function<void()> printFirst) {
// cout<<1<<endl;
// printFirst() outputs "first". Do not change or remove this line.
// mtx.lock();
printFirst();
flag=1;
// mtx.unlock();
cond.notify_all();
// cout<<11<<endl;
}
void second(function<void()> printSecond) {
// cout<<2<<endl;
{
unique_lock<mutex> lk(mtx);
cond.wait(lk,[this](){return flag==1;});
// printSecond() outputs "second". Do not change or remove this line.
printSecond();
flag=2;
}
// cout<<22<<endl;
cond.notify_all();
}
void third(function<void()> printThird) {
// cout<<3<<endl;
unique_lock<mutex> lk(mtx);
cond.wait(lk,[this](){return flag==2;});
// printThird() outputs "third". Do not change or remove this line.
printThird();
flag=3;
// cout<<33<<endl;
}
mutex mtx;
condition_variable cond;
int flag=0;
};
3 Réponses :
C'est nécessaire. P>
que votre code produit une sortie correcte dans cette instance est en plus du point. Il est tout à fait éventuellement que printfirst code> pourrait ne pas être complet à l'heure
imprimésecond code> est appelé. Vous avez besoin du mutex pour éviter cela et arrêter
imprimésecond code> et
printThird code>. d'être couru en même temps. P>
Je ne peux pas être d'accord. Même si imprimésecond code> est appelé d'abord, il sera bloqué jusqu'à ce que
PrintFirst code> Terminé. @QctDev
ahh je vois le cond.Wait code> maintenant. Avec cela, vous n'aurez pas besoin du mutex ou des autres serrures. Je préférerais le paradigme Mutex / Lock, car votre intention est plus évidente.
@QctDev Il aura toujours besoin du mutex pour s'assurer que l'écriture et la lecture de drapeau code> ne se produit pas en même temps.
@Tedlyngmo Si c'est juste drapeau code> laissé pour protéger puis
drapeau code> doit être effectué
atomique code>.
@QCTDev qui introduirait un niveau de verrouillage sur le mutex déjà existant censé protéger drapeau code> que
second code> et
troisième code> utilise. La bonne façon de le faire serait d'utiliser le même motif de verrouillage dans
d'abord code> comme utilisé dans
secondaire code> et
troisième code>.
@Tedlyngmo Je ne dis pas que ce n'est pas le cas. Nous convenons que le mutex est la solution optimale.
Évidemment, vos fonctions de trois éléments sont censées être appelées par différents threads. Ainsi, vous devez verrouiller le mutex dans chaque thread pour protéger la variable commune Assurez-vous toujours de déverrouiller le mutex avant d'appeler mettre un indicateur code> de l'accès simultané. Donc, vous devriez noter
mtx.lock () code> et
mtx.unlock () code> dans
premier code> pour le protéger également. Fonctions
secondes code> et
troisième code> Appliquez un
unique_lock code> comme alternative pour cela. p>
cond.notify_all () code> soit en appelant
mtx.unlock () code> avant ou en faisant le
unique_lock code > une variable locale d'un bloc de code interne comme dans
second code>. p>
Conseil supplémentaire h2>
privé: code> avant les variables d'élément au bas de la définition de votre classe pour les protéger de l'accès extérieur. Cela garantira que
indicateur code> ne peut pas être modifié sans verrouiller le mutex. P>
Bon conseil +1.
"Assurez-vous toujours de déverrouiller le mutex avant d'appeler Cond.Notify_All ()" - Pourquoi? Afaik n'est pas nécessaire.
@sklott "Le fil notifiant n'a pas besoin de maintenir le verrou sur le même mutex que celui détenu par le ou les filets d'attente; en fait le cas d'une pessimisation, puisque le fil notifié bloquera immédiatement, en attente de la notification fil pour libérer la serrure. "
"Pas besoin de" et "ne devrait pas être" est un peu différent, hein?
@sklott Ouais la déclaration est vague dans la réponse.
Et " notifiant pendant que, sous la serrure, peut néanmoins être nécessaire lorsque la planification précise des événements est requise, par exemple si le fil d'attente quitte le programme si la condition est satisfaite, causant la destruction de la condition de filetage de notification. Un réveil parasite après le mutex. Déverrouiller mais avant de notifier n'entraînerait pas l'avertissement appelé un objet détruit. I> "- ces réveilles parasites sont terribles
Si je ne verrouille pas le mutex, y a-t-il une erreur logique? Pouvez-vous faire un exemple? MERCI!
@Alfred Si vous ne verrouillez pas le mutex, vous pouvez attribuer une valeur à drapeau code> en même temps qu'un autre thread se lit
drapeau code> comme je l'ai expliqué dans ma réponse.
@Alfred: Imaginez votre drapeau code> est par exemple un énorme
vecteur code> et une fonction va changer tous les éléments. Maintenant, lorsque cette fonction a changé la moitié des éléments, une autre fonction dans un autre thread est exécutée et lit la moitié des valeurs anciennes et la moitié des nouvelles valeurs, ce qui serait incompatible. Le mutex empêchera cela. Bien qu'il soit improbable que cela arrive à votre drapeau
code>, il peut également se produire et serait un code risqué de ne pas empêcher cela.
J'ai compris! Merci!
Grande note sur l'obtention du mutex. Les variables partagées protégées par un mutex doivent utiliser le mutex sur chaque accès, même si ce n'est pas évidemment nécessaire, car il empêche également le compilateur et la CPU d'optimiser les lectures de la variable et le cache de rétablissement de la mémoire principale.
Le signal d'appel en dehors de la section critique est un bon conseil, mais peut ne pas entraîner la meilleure performance. Il devrait théoriquement empêcher un sommeil supplémentaire. Cependant, le système d'exploitation peut tenter de planifier "intelligemment". Mettre fin à la section critique (en déverrouillant le mutex) peut être perçue comme une bonne opportunité pour un commutateur de contexte. Dans ce cas, le thread sera préempté avant d'appelle signal code>, entraînant un peu plus long de retard avant que le fil d'attente puisse s'exécuter. Je conseillerais toujours de signaler à l'extérieur du mutex, avec la mise en garde qu'il n'est pas nécessaire et non toujours le meilleur plan d'action.
Le drapeau réécrire ce p> comme ça et il peut être plus facile Pour voir: P> Il fait la même chose que votre code> Vérification de la condition dans
secondaire () code> ou
troisième () code> peut être évalué en même temps que
premier () code> attribue
1 code> à
indicateur code>.
attendre () code> avec un lambda. p>
drapeau Code> est censé être lu alors que le mutex est maintenu - mais
premier code> ne se soucie pas des mutiles et des attributs à
drapeau code> chaque fois qu'il veut. Pour les types non atomiques, c'est une catastrophe. Il peut travailler em> 10000000 fois (et probablement) - mais quand les choses se produisent en même temps (parce que vous le laissez) -
Même les types atomiques présentent cette condition de course
@Alanbirtles, je me faisais simplement pointer sur la course lors de ses opérations de R / W simultanées dans le même lieu de mémoire qui conduirait à UB - et que c'était un atomique, cela ne se produirait pas.
Veuillez poster un exemple de reproductible minimal . Au fur et à mesure que votre classe ne contient aucun threads, cela n'a pas besoin de mutiles, cependant, si votre classe est appelée à partir de threads multiples, cela pourrait nécessiter des mutiles.