2010-02-07 5 views
6

Imaginez un programme avec deux threads. Ils sont en cours d'exécution le code suivant (CAS fait référence à Compare and Swap):Les opérations atomiques concurrentes peuvent-elles s'affaiblir les unes les autres?

// Visible to both threads 
static int test; 

// Run by thread A 
void foo() 
{ 
    // Check if value is 'test' and swap in 0xdeadbeef 
    while(!CAS(&test, test, 0xdeadbeef)) {} 
} 

// Run by thread B 
void bar() 
{ 
    while(1) { 
     // Perpetually atomically write rand() into the test variable 
     atomic_write(&test, rand()); 
    } 
} 

Est-il possible pour le fil B pour provoquer perpétuellement CAS de fil A à l'échec de telle sorte que 0xdeadbeef est jamais écrit « test »? Ou est-ce que la gigue d'ordonnancement naturel signifie que dans la pratique cela n'arrive jamais? Que faire si un travail a été fait à l'intérieur de la boucle while du thread A?

Répondre

6

En théorie, oui. Si vous pouviez gérer d'une façon ou d'une autre pour obtenir les deux threads fonctionnant comme ceci:

Alors CAS ne reviendrait jamais vrai.

En pratique, cela n'arrivera jamais lorsque des threads partagent un CPU/Core, et il est peu probable que cela se produise lorsque des threads sont exécutés sur des processeurs ou des cœurs différents. En pratique, c'est incroyablement improbable pour plus de quelques cycles, et astronomiquement peu probable pour plus d'un quantum de planificateur.

Et c'est si ce code

void foo() 
{ 
    // Check if value is 'test' and swap in 0xdeadbeef 
    while(!CAS(&test, test, 0xdeadbeef)) {} 
} 

fait ce qu'il semble faire, ce qui est de récupérer la valeur actuelle de test, et le comparer à test pour voir si elle a changé. Dans le monde réel, les itérations de CAS seraient séparées par un code qui a réellement fonctionné. Le mot-clé volatile serait nécessaire pour s'assurer que le compilateur récupérait le test avant d'appeler CAS plutôt que de supposer qu'une copie qu'il pourrait encore avoir dans un registre serait toujours valide.

Ou la valeur que vous testerez contre ne serait pas la valeur actuelle de test, mais plutôt une sorte de dernière connue valeur. En d'autres termes, cet exemple de code est un test de la théorie, mais vous n'utiliseriez pas CAS comme ça en pratique, donc même si vous pouviez faire échouer cela, cela ne vous dit pas nécessairement comment il pourrait échouer lorsqu'il est utilisé dans des algorithmes du monde réel.

+0

Pourquoi ne l'exécuterait que deux fois? Si elle optimise la récupération de test, n'est-ce pas l'effet qu'elle pourrait boucler * pour toujours *? La comparaison et l'échange échoueront toujours parce que la comparaison sera perpétuellement fausse, sauf si vous avez de la chance et rand() retourne la valeur à laquelle le test se trouvait avant que le thread A entre dans la boucle et le thread A s'exécute au bon moment. –

+0

Aussi quand vous dites incroyablement peu probable, voulez-vous dire assez peu probable que vous ne seriez pas opposé à l'utilisation de ce code dans le logiciel de production? Il serait intéressant d'essayer de calculer une probabilité simplifiée. –

+0

Quand je dis personnellement incroyablement peu probable en se référant à un problème de filetage, je veux dire que je veux le réparer afin qu'il ne se produise vraiment pas. J'ai eu trop de "ne va pas arriver" exploser sur moi. –

2

La famine peut sûrement arriver dans de tels cas. Citant the wikipedia page,

Il a également été démontré que les conditionnelles atomiques largement disponibles primitives, CAS et LL/SC, ne peuvent pas fournir sans famine mises en œuvre de nombreuses données communes structures sans coûts de mémoire de plus en plus linéairement dans le nombre de threads . Algorithmes sans attente sont donc rares, à la fois dans la recherche et en pratique.

(Voir le lien de la page en question pour la preuve mathématique).

+0

En fonction de la façon dont le CAS est utilisé, il est souvent possible de s'assurer qu'aucune tâche ne sera bloquée, sauf si une autre tâche avance. Si l'ensemble des travaux à effectuer est fini, une telle assurance signifie à son tour que chaque tâche sera terminée. La durée maximale d'exécution d'une tâche particulière peut être calculée en fonction de la quantité totale de travail qui va arriver.Dans un système sans alimentation, le temps d'achèvement d'une tâche une fois démarré serait borné même si la charge de travail ne l'est pas, mais le fait d'avoir un temps limité par rapport à la charge de travail est toujours utile. – supercat

Questions connexes