Qu'est-ce qu'une condition de concurrence?


982

Lors de l'écriture d'applications multithreads, l'un des problèmes les plus courants rencontrés est les conditions de concurrence.

Mes questions à la communauté sont:

Quelle est la condition de course?
Comment les détectez-vous?
Comment les gérez-vous?
Enfin, comment les empêchez-vous de se produire?


3
Il y a un grand chapitre dans le HOWTO de programmation sécurisée pour Linux qui décrit ce qu'ils sont et comment les éviter.
Craig H

4
Je voudrais mentionner que - sans préciser la langue - la plupart des parties de cette question ne peuvent pas être répondues correctement, car dans différentes langues, la définition, les conséquences et les outils pour les prévenir peuvent différer.
MikeMB

@MikeMB. D'accord, sauf lors de l'analyse de l'exécution du code octet, comme cela est fait par Race Catcher (voir ce thread stackoverflow.com/a/29361427/1363844 ), nous pouvons traiter tous ces environ 62 langues qui se compilent en code octet (voir en.wikipedia.org / wiki / List_of_JVM_languages )
Ben

Réponses:


1238

Une condition de concurrence critique se produit lorsque deux threads ou plus peuvent accéder aux données partagées et qu'ils essaient de les modifier en même temps. Étant donné que l'algorithme de planification des threads peut à tout moment basculer entre les threads, vous ne connaissez pas l'ordre dans lequel les threads tenteront d'accéder aux données partagées. Par conséquent, le résultat de la modification des données dépend de l'algorithme de programmation des threads, c'est-à-dire que les deux threads sont "en course" pour accéder / modifier les données.

Des problèmes surviennent souvent lorsqu'un thread effectue un "check-then-act" (par exemple "check" si la valeur est X, puis "agir" pour faire quelque chose qui dépend de la valeur étant X) et un autre thread fait quelque chose à la valeur dans entre le "chèque" et l '"acte". Par exemple:

if (x == 5) // The "Check"
{
   y = x * 2; // The "Act"

   // If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
   // y will not be equal to 10.
}

Le fait est que y pourrait être 10, ou n'importe quoi, selon qu'un autre thread a changé x entre la vérification et l'acte. Vous n'avez aucun moyen réel de savoir.

Afin d'éviter que des conditions de concurrence critique ne se produisent, vous devez généralement verrouiller les données partagées pour vous assurer qu'un seul thread peut accéder aux données à la fois. Cela signifierait quelque chose comme ceci:

// Obtain lock for x
if (x == 5)
{
   y = x * 2; // Now, nothing can change x until the lock is released. 
              // Therefore y = 10
}
// release lock for x

121
Que fait l'autre thread lorsqu'il rencontre le verrou? Attend-il? Erreur?
Brian Ortiz

174
Oui, l'autre thread devra attendre que le verrou soit libéré avant de pouvoir continuer. Il est donc très important que le verrou soit libéré par le fil de maintien lorsqu'il en a fini avec lui. S'il ne le libère jamais, l'autre thread attendra indéfiniment.
Lehane

2
@Ian Dans un système multithread, il y aura toujours des moments où les ressources devront être partagées. Dire qu'une approche est mauvaise sans donner d'alternative n'est tout simplement pas productif. Je cherche toujours des moyens de m'améliorer et s'il y a une alternative, je la rechercherai avec plaisir et j'évaluerai les avantages et les inconvénients.
Despertar

2
@Despertar ... aussi, ce n'est pas nécessairement le cas que les ressources devront toujours être partagées dans un système milti-threadé. Par exemple, vous pourriez avoir un tableau où chaque élément doit être traité. Vous pouvez éventuellement partitionner le tableau et avoir un thread pour chaque partition et les threads peuvent faire leur travail de manière complètement indépendante les uns des autres.
Ian Warburton

12
Pour qu'une course se produise, il suffit qu'un seul thread tente de modifier les données partagées tandis que les autres threads peuvent les lire ou les modifier.
SomeWittyUsername

213

Une «condition de concurrence critique» existe lorsqu'un code multithread (ou autrement parallèle) qui accéderait à une ressource partagée pourrait le faire de manière à provoquer des résultats inattendus.

Prenez cet exemple:

for ( int i = 0; i < 10000000; i++ )
{
   x = x + 1; 
}

Si vous aviez 5 threads exécutant ce code à la fois, la valeur de x NE SERAIT PAS 50 000 000. En fait, cela varierait à chaque passage.

C'est parce que, pour que chaque thread incrémente la valeur de x, ils doivent faire ce qui suit: (simplifié, évidemment)

Récupérer la valeur de x
Ajoutez 1 à cette valeur
Stockez cette valeur dans x

N'importe quel thread peut être à n'importe quelle étape de ce processus à tout moment et ils peuvent marcher l'un sur l'autre lorsqu'une ressource partagée est impliquée. L'état de x peut être modifié par un autre thread pendant la période entre la lecture de x et sa réécriture.

Disons qu'un thread récupère la valeur de x, mais ne l'a pas encore stockée. Un autre thread peut également récupérer la même valeur de x (car aucun thread ne l'a encore changé) et ils stockeront tous les deux la même valeur (x + 1) dans x!

Exemple:

Thread 1: lit x, la valeur est 7
Sujet 1: ajoutez 1 à x, la valeur est maintenant 8
Thread 2: lit x, la valeur est 7
Fil 1: stocke 8 dans x
Thread 2: ajoute 1 à x, la valeur est désormais 8
Fil 2: stocke 8 en x

Les conditions de concurrence peuvent être évitées en utilisant une sorte de mécanisme de verrouillage avant le code qui accède à la ressource partagée:

for ( int i = 0; i < 10000000; i++ )
{
   //lock x
   x = x + 1; 
   //unlock x
}

Ici, la réponse ressort à 50 000 000 à chaque fois.

Pour plus d'informations sur le verrouillage, recherchez: mutex, sémaphore, section critique, ressource partagée.


Voir jakob.engbloms.se/archives/65 pour un exemple de programme permettant de tester à quel point de telles choses vont mal ... cela dépend vraiment du modèle de mémoire de la machine sur laquelle vous utilisez.
jakobengblom2

1
Comment peut-il atteindre 50 millions s'il doit s'arrêter à 10 millions?

9
@nocomprende: Par 5 threads exécutant le même code à la fois, comme décrit directement sous l'extrait ...
Jon Skeet

4
@ JonSkeet Vous avez raison, j'ai confondu le i et le x. Je vous remercie.

Le double contrôle du verrouillage dans l'implémentation du modèle Singleton est un exemple de prévention de la condition de concurrence.
Bharat Dodeja

150

Qu'est-ce qu'une condition de course?

Vous prévoyez d'aller voir un film à 17 h. Vous vous renseignez sur la disponibilité des billets à 16h. Le représentant dit qu'ils sont disponibles. Vous vous détendez et atteignez le guichet 5 minutes avant le spectacle. Je suis sûr que vous pouvez deviner ce qui se passe: c'est une maison pleine. Le problème ici était dans la durée entre le contrôle et l'action. Vous vous êtes renseigné à 4 heures et avez agi à 5 heures. Entre-temps, quelqu'un d'autre a saisi les billets. C'est une condition de course - spécifiquement un scénario "check-then-act" des conditions de course.

Comment les détectez-vous?

Révision du code religieux, tests unitaires multithread. Il n'y a pas de raccourci. Il y a peu de plugins Eclipse émergeant à ce sujet, mais rien de stable pour le moment.

Comment les gérez-vous et les prévenez-vous?

La meilleure chose serait de créer des fonctions sans effets secondaires et sans état, d'utiliser autant que possible des immuables. Mais ce n'est pas toujours possible. Donc, en utilisant java.util.concurrent.atomic, des structures de données simultanées, une synchronisation appropriée et une concurrence basée sur les acteurs vous aideront.

La meilleure ressource pour la concurrence est JCIP. Vous pouvez également obtenir plus de détails sur l'explication ci-dessus ici .


Les révisions de code et les tests unitaires sont secondaires pour modéliser le flux entre vos oreilles et utiliser moins la mémoire partagée.
Acumenus

2
J'ai apprécié l'exemple du monde réel d'une condition de course
Tom O.

11
Comme la réponse, le pouce levé . La solution est: vous verrouillez les tickets entre 4 et 5 avec mutex (exception mutuelle, c ++). Dans le monde réel, cela s'appelle la réservation de billets :)
Volt

1
serait une réponse décente si vous supprimiez les bits uniquement Java (la question ne concerne pas Java, mais plutôt les conditions de course en général)
Corey Goldberg

Non, ce n'est pas une condition de course. Du point de vue "commercial", vous avez juste attendu trop longtemps. De toute évidence, les commandes en souffrance ne sont pas une solution. Essayez un scalper sinon achetez simplement le billet comme assurance
csherriff

65

Il existe une différence technique importante entre les conditions de course et les courses de données. La plupart des réponses semblent supposer que ces termes sont équivalents, mais ils ne le sont pas.

Une course aux données se produit lorsque 2 instructions accèdent au même emplacement de mémoire, au moins un de ces accès est une écriture et il n'y a aucun événement avant de passer une commande parmi ces accès. Maintenant, ce qui constitue un événement avant la commande est sujet à de nombreux débats, mais en général, les paires ulock-lock sur la même variable de verrouillage et les paires de signaux d'attente sur la même variable de condition induisent un ordre passe-avant.

Une condition de concurrence est une erreur sémantique. Il s'agit d'une faille qui se produit dans le calendrier ou l'ordre des événements qui conduit à un comportement erroné du programme .

De nombreuses conditions de course peuvent être (et sont en fait) causées par des courses de données, mais ce n'est pas nécessaire. En fait, les courses de données et les conditions de course ne sont ni nécessaires ni suffisantes les unes pour les autres. Ce billet de blog explique également très bien la différence, avec un simple exemple de transaction bancaire. Voici un autre exemple simple qui explique la différence.

Maintenant que nous avons cloué la terminologie, essayons de répondre à la question initiale.

Étant donné que les conditions de concurrence sont des bogues sémantiques, il n'existe aucun moyen général de les détecter. En effet, il n'y a aucun moyen d'avoir un oracle automatisé qui puisse distinguer le comportement correct du programme incorrect dans le cas général. La détection des races est un problème indécidable.

D'un autre côté, les races de données ont une définition précise qui ne se rapporte pas nécessairement à l'exactitude, et donc on peut les détecter. Il existe de nombreuses variantes de détecteurs de course de données (détection de course de données statique / dynamique, détection de course de données basée sur un jeu de clés, détection de course de données basée sur le passé, détection de course de données hybride). Un détecteur de course de données dynamique de pointe est ThreadSanitizer qui fonctionne très bien dans la pratique.

La gestion des courses de données en général nécessite une certaine discipline de programmation pour induire des fronts entre les accès aux données partagées (soit pendant le développement, soit une fois qu'ils sont détectés à l'aide des outils mentionnés ci-dessus). cela peut être fait à travers des verrous, des variables de condition, des sémaphores, etc. Cependant, on peut également utiliser différents paradigmes de programmation comme le passage de messages (au lieu de la mémoire partagée) qui évitent les courses de données par construction.


La différence est essentielle pour comprendre la condition de course. Merci!
ProgramCpp le

37

Une sorte de définition canonique est « lorsque deux threads accèdent au même emplacement en mémoire en même temps, et qu'au moins un des accès est une écriture ». Dans la situation, le thread "lecteur" peut obtenir l'ancienne ou la nouvelle valeur, selon le thread "qui remporte la course". Ce n'est pas toujours un bogue - en fait, certains algorithmes de bas niveau vraiment poilus le font exprès - mais il devrait généralement être évité. @Steve Gury donne un bon exemple de cas où cela pourrait être un problème.


3
Pourriez-vous s'il vous plaît donner un exemple de la façon dont les conditions de course peuvent être utiles? La recherche sur Google n'a pas aidé.
Alex V.

3
@Alex V. À ce stade, je n'ai aucune idée de quoi je parlais. Je pense que cela a peut-être été une référence à une programmation sans verrouillage, mais il n'est pas vraiment exact de dire que cela dépend des conditions de course, en soi.
Chris Conway

33

Une condition de concurrence est une sorte de bug, qui ne se produit qu'avec certaines conditions temporelles.

Exemple: imaginez que vous avez deux fils, A et B.

Dans le fil A:

if( object.a != 0 )
    object.avg = total / object.a

Dans le fil B:

object.a = 0

Si le thread A est préempté juste après avoir vérifié cet objet. A n'est pas nul, B le fera a = 0, et lorsque le thread A gagnera le processeur, il fera une "division par zéro".

Ce bogue ne se produit que lorsque le thread A est préempté juste après l'instruction if, c'est très rare, mais cela peut arriver.


21

La condition de concurrence n'est pas seulement liée au logiciel mais également au matériel. En fait, le terme a été initialement inventé par l'industrie du matériel.

Selon wikipedia :

Le terme trouve son origine dans l'idée que deux signaux s'emballent pour influencer la sortie en premier .

Condition de course dans un circuit logique:

entrez la description de l'image ici

L'industrie du logiciel a utilisé ce terme sans modification, ce qui le rend un peu difficile à comprendre.

Vous devez effectuer un remplacement pour le mapper au monde logiciel:

  • "deux signaux" => "deux fils" / "deux processus"
  • "influencer la sortie" => "influencer un état partagé"

Ainsi, la condition de concurrence dans l'industrie du logiciel signifie "deux threads" / "deux processus" qui s'affrontent pour "influencer un état partagé", et le résultat final de l'état partagé dépendra d'une certaine différence de synchronisation subtile, qui pourrait être causée par certains ordre de lancement des threads / processus, ordonnancement des threads / processus, etc.


20

Une condition de concurrence critique est une situation de programmation simultanée où deux threads ou processus simultanés sont en concurrence pour une ressource et l'état final résultant dépend de qui obtient la ressource en premier.


explication juste brillante
fok

État final de quoi?
Roman Alexandrovich

1
@RomanAlexandrovich L'état final du programme. L'état se référant à des choses telles que les valeurs des variables, etc. Voir l'excellente réponse de Lehane. L '"état" dans son exemple ferait référence aux valeurs finales de' x 'et' y '.
AMTerp

19

Les conditions de concurrence se produisent dans les applications multi-thread ou les systèmes multi-processus. Une condition de concurrence, à sa base, est tout ce qui fait l'hypothèse que deux choses qui ne sont pas dans le même thread ou processus se produiront dans un ordre particulier, sans prendre de mesures pour s'assurer qu'elles le font. Cela se produit généralement lorsque deux threads transmettent des messages en définissant et en vérifiant les variables membres d'une classe auxquelles les deux peuvent accéder. Il y a presque toujours une condition de concurrence lorsqu'un thread appelle sleep pour donner à un autre thread le temps de terminer une tâche (sauf si ce sommeil est en boucle, avec un mécanisme de vérification).

Les outils de prévention des conditions de concurrence dépendent du langage et du système d'exploitation, mais certains sont communs aux mutex, aux sections critiques et aux signaux. Les mutex sont bons lorsque vous voulez vous assurer que vous êtes le seul à faire quelque chose. Les signaux sont bons lorsque vous voulez vous assurer que quelqu'un d'autre a fini de faire quelque chose. La réduction des ressources partagées peut également aider à prévenir les comportements inattendus

Détecter les conditions de course peut être difficile, mais il y a quelques signes. Le code qui dépend fortement du sommeil est sujet aux conditions de concurrence, alors vérifiez d'abord les appels à dormir dans le code affecté. L'ajout de périodes de sommeil particulièrement longues peut également être utilisé pour le débogage pour essayer de forcer un ordre particulier d'événements. Cela peut être utile pour reproduire le comportement, voir si vous pouvez le faire disparaître en modifiant le timing des choses et pour tester les solutions mises en place. Les mises en veille doivent être supprimées après le débogage.

Le signe de signature que l'on a une condition de concurrence est cependant, s'il y a un problème qui ne se produit que par intermittence sur certaines machines. Les bogues courants seraient les plantages et les blocages. Avec la journalisation, vous devriez être en mesure de trouver la zone affectée et de travailler à partir de là.


10

Microsoft a en fait publié un article vraiment détaillé sur cette question des conditions de concurrence et des blocages. Le résumé le plus résumé serait le paragraphe de titre:

Une condition de concurrence critique se produit lorsque deux threads accèdent à une variable partagée en même temps. Le premier thread lit la variable et le second thread lit la même valeur dans la variable. Ensuite, le premier thread et le deuxième thread effectuent leurs opérations sur la valeur, et ils se précipitent pour voir quel thread peut écrire la dernière valeur dans la variable partagée. La valeur du thread qui écrit sa dernière valeur est conservée, car le thread écrit sur la valeur écrite par le thread précédent.


5

Qu'est-ce qu'une condition de concurrence?

La situation où le processus dépend de façon critique de la séquence ou du calendrier d'autres événements.

Par exemple, le processeur A et le processeur B ont tous deux besoin de ressources identiques pour leur exécution.

Comment les détectez-vous?

Il existe des outils pour détecter automatiquement la condition de concurrence:

Comment les gérez-vous?

Les conditions de compétition peuvent être gérées par Mutex ou Semaphores . Ils agissent comme un verrou permettant à un processus d'acquérir une ressource en fonction de certaines exigences pour empêcher la condition de concurrence.

Comment les empêchez-vous de se produire?

Il existe différentes façons d'empêcher la condition de concurrence, comme l' évitement des sections critiques .

  1. Pas deux processus simultanément à l'intérieur de leurs régions critiques. ( Exclusion mutuelle)
  2. Aucune hypothèse n'est faite sur les vitesses ou le nombre de CPU.
  3. Aucun processus s'exécutant en dehors de sa région critique qui bloque d'autres processus.
  4. Aucun processus ne doit attendre indéfiniment pour entrer dans sa région critique. (A attend les ressources B, B attend les ressources C, C attend les ressources A)

2

Une condition de concurrence critique est une situation indésirable qui se produit lorsqu'un appareil ou un système tente d'effectuer deux ou plusieurs opérations en même temps, mais en raison de la nature de l'appareil ou du système, les opérations doivent être effectuées dans le bon ordre afin d'être fait correctement.

Dans la mémoire ou le stockage de l'ordinateur, une condition de concurrence peut se produire si des commandes de lecture et d'écriture d'une grande quantité de données sont reçues presque au même instant et que la machine tente d'écraser une partie ou la totalité des anciennes données pendant que ces anciennes données sont toujours en cours. lis. Le résultat peut être un ou plusieurs des éléments suivants: un crash informatique, une «opération illégale», une notification et un arrêt du programme, des erreurs de lecture des anciennes données ou des erreurs d'écriture des nouvelles données.


2

Voici l'exemple classique de solde de compte bancaire qui aidera les débutants à comprendre les threads en Java facilement dans les conditions de concurrence:

public class BankAccount {

/**
 * @param args
 */
int accountNumber;
double accountBalance;

public synchronized boolean Deposit(double amount){
    double newAccountBalance=0;
    if(amount<=0){
        return false;
    }
    else {
        newAccountBalance = accountBalance+amount;
        accountBalance=newAccountBalance;
        return true;
    }

}
public synchronized boolean Withdraw(double amount){
    double newAccountBalance=0;
    if(amount>accountBalance){
        return false;
    }
    else{
        newAccountBalance = accountBalance-amount;
        accountBalance=newAccountBalance;
        return true;
    }
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    BankAccount b = new BankAccount();
    b.accountBalance=2000;
    System.out.println(b.Withdraw(3000));

}

1

Vous pouvez empêcher la condition de concurrence , si vous utilisez des classes "atomiques". La raison en est simplement que le thread ne sépare pas l'opération get and set, l'exemple est ci-dessous:

AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);

En conséquence, vous aurez 7 dans le lien "ai". Bien que vous ayez effectué deux actions, mais que les deux opérations confirment le même thread et qu'aucun autre thread n'interfère avec cela, cela ne signifie aucune condition de concurrence!


0

Essayez cet exemple de base pour mieux comprendre les conditions de concurrence:

    public class ThreadRaceCondition {

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        Account myAccount = new Account(22222222);

        // Expected deposit: 250
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.DEPOSIT, 5.00);
            t.start();
        }

        // Expected withdrawal: 50
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.WITHDRAW, 1.00);
            t.start();

        }

        // Temporary sleep to ensure all threads are completed. Don't use in
        // realworld :-)
        Thread.sleep(1000);
        // Expected account balance is 200
        System.out.println("Final Account Balance: "
                + myAccount.getAccountBalance());

    }

}

class Transaction extends Thread {

    public static enum TransactionType {
        DEPOSIT(1), WITHDRAW(2);

        private int value;

        private TransactionType(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    };

    private TransactionType transactionType;
    private Account account;
    private double amount;

    /*
     * If transactionType == 1, deposit else if transactionType == 2 withdraw
     */
    public Transaction(Account account, TransactionType transactionType,
            double amount) {
        this.transactionType = transactionType;
        this.account = account;
        this.amount = amount;
    }

    public void run() {
        switch (this.transactionType) {
        case DEPOSIT:
            deposit();
            printBalance();
            break;
        case WITHDRAW:
            withdraw();
            printBalance();
            break;
        default:
            System.out.println("NOT A VALID TRANSACTION");
        }
        ;
    }

    public void deposit() {
        this.account.deposit(this.amount);
    }

    public void withdraw() {
        this.account.withdraw(amount);
    }

    public void printBalance() {
        System.out.println(Thread.currentThread().getName()
                + " : TransactionType: " + this.transactionType + ", Amount: "
                + this.amount);
        System.out.println("Account Balance: "
                + this.account.getAccountBalance());
    }
}

class Account {
    private int accountNumber;
    private double accountBalance;

    public int getAccountNumber() {
        return accountNumber;
    }

    public double getAccountBalance() {
        return accountBalance;
    }

    public Account(int accountNumber) {
        this.accountNumber = accountNumber;
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean deposit(double amount) {
        if (amount < 0) {
            return false;
        } else {
            accountBalance = accountBalance + amount;
            return true;
        }
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean withdraw(double amount) {
        if (amount > accountBalance) {
            return false;
        } else {
            accountBalance = accountBalance - amount;
            return true;
        }
    }
}

0

Vous ne voulez pas toujours ignorer une condition de concurrence. Si vous avez un indicateur qui peut être lu et écrit par plusieurs threads et que cet indicateur est défini sur "terminé" par un thread afin que l'autre thread arrête le traitement lorsque l'indicateur est défini sur "terminé", vous ne voulez pas de cette "course". condition "à éliminer. En fait, celui-ci peut être qualifié de condition de course bénigne.

Cependant, en utilisant un outil pour la détection de la condition de course, il sera identifié comme une condition de course nuisible.

Plus de détails sur les conditions de compétition ici, http://msdn.microsoft.com/en-us/magazine/cc546569.aspx .


Sur quelle langue repose votre réponse?
MikeMB

Franchement, il me semble que si vous avez des conditions de concurrence en soi , vous ne construisez pas votre code de manière strictement contrôlée. Ce qui, bien que cela ne soit pas un problème dans votre cas théorique, est la preuve de problèmes plus importants avec la façon dont vous concevez et développez le logiciel. Attendez-vous à être confronté tôt ou tard à des problèmes de race douloureux.
Ingénieur

0

Considérons une opération qui doit afficher le nombre dès que le nombre est incrémenté. c'est-à-dire, dès que CounterThread incrémente la valeur DisplayThread doit afficher la valeur récemment mise à jour.

int i = 0;

Production

CounterThread -> i = 1  
DisplayThread -> i = 1  
CounterThread -> i = 2  
CounterThread -> i = 3  
CounterThread -> i = 4  
DisplayThread -> i = 4

Ici, CounterThread obtient fréquemment le verrou et met à jour la valeur avant que DisplayThread ne l' affiche. Il existe une condition de course. La condition de concurrence peut être résolue en utilisant la synchronisation


0

Une condition de concurrence critique est une situation indésirable qui se produit lorsque deux processus ou plus peuvent accéder et modifier les données partagées en même temps, car il y a eu des accès conflictuels à une ressource. Un problème de section critique peut entraîner une condition de concurrence. Pour résoudre une condition critique parmi le processus, nous n'avons supprimé qu'un seul processus à la fois qui exécute la section critique.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.