2011-02-08 2 views
1

Une question similaire a été posée here, mais les réponses semblent généralement toutes se rapporter à la notation lambda. Je reçois un résultat similaire sans le lambda, donc je pensais que je vais demander des éclaircissements:Conditions de course pour le démarrage du thread

Dire que j'ai quelque chose comme ceci:

for (int i = 0; i < 5; i++) 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + i); 
    }))).Start(); 

On pourrait attendre la sortie suivante:

Thread 0 
Thread 1 
Thread 2 
Thread 3 
Thread 4 

Maintenant, je me rends compte que les threads ne sont pas démarrés dans un ordre particulier, alors supposons que les lignes ci-dessus peuvent sortir dans n'importe quel ordre.

Mais ce n'est pas ce qui se passe. Qu'arrive-t- place:

Thread 3 
Thread 4 
Thread 4 
Thread 4 
Thread 4 

ou quelque chose de similaire, ce qui me porte à croire que, plutôt que de passer la valeur si i, il passe la référence. (Ce qui est bizarre, car un int est un type de valeur).

Faire quelque chose comme ceci:

for (int i = 0; i < 5; i++) 
    (new Thread(new ThreadStart(delegate() 
    { 
     int j = i; 
     Console.WriteLine("Thread " + j); 
    }))).Start(); 

ne vous aide pas non plus, même si nous avons fait une copie de i. Je suppose que la raison en est qu'il n'a pas fait une copie de moi à temps.

Faire quelque chose comme ceci:

for (int i = 0; i < 5; i++) 
{ 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + i); 
    }))).Start(); 
    Thread.Sleep(50); 
} 

semble résoudre le problème, mais il est extrêmement indésirable que nous gaspillons 50ms à chaque itération, pour ne pas mentionner le fait que si l'ordinateur est lourdement chargé alors peut-être que 50ms ne suffiront peut-être pas.

Voici un exemple avec mon problème actuel, spécifique:

Thread t = new Thread(new ThreadStart(delgate() 
    { 
    threadLogic(param1, param2, param3, param4); 
    })); 
t.Start(); 

param1 = param2 = param3 = param4 = null; 

avec:

void threadLogic(object param1, object param2, object param3, object param4) 
{ 
    // Do some stuff here... 
} 

Je veux threadLogic() pour exécuter dans son propre thread, mais le code ci-dessus donne une valeur nulle exception de référence. Je suppose que c'est parce que les valeurs sont définies sur null avant que le thread a eu une chance de démarrer.

Encore une fois, mettre un Thread.Sleep (100) fonctionne, mais c'est une solution terrible de tous les aspects. Qu'est-ce que vous recommandez pour ce type particulier de condition de course?

+0

Affichez http://stackoverflow.com/questions/1930133/c-closures-why-is-the-loopvariable-captured-by-reference et http://stackoverflow.com/ questions/1923577/different-behavior-when-starting-a-thread-paramétréthreadstart-vs-anonymous/ – nos

Répondre

3

Vous devez introduire un temporaire:

for (int i = 0; i < 5; i++) 
{ 
    int temp = i; // Add this 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + temp); 
    }))).Start(); 
} 

Le problème est dans la façon dont les délégués se referment sur la variable externe (i dans votre code, temp dans le mien). La portée est incorrecte (en dehors de la boucle for), donc au moment où le thread démarre, i a déjà été incrémenté la plupart du temps, sinon tout le chemin.


Pour votre deuxième exemple, vous devez faire la même chose. Il suffit de faire: temporaires

var temp1 = param1; 
var temp2 = param2; 
var temp3 = param3; 
var temp4 = param4; 
Thread t = new Thread(new ThreadStart(delgate() 
    { 
    threadLogic(temp1, temp2, temp3, temp4); 
    })); 
t.Start(); 

// This is now safe, since the closure above is over "temp*" 
param1 = param2 = param3 = param4 = null; 
+0

Merci, c'est une excellente solution pour l'exemple simple que j'ai donné. Que diriez-vous de l'autre problème avec les paramètres de thread étant mis à null immédiatement après t.Start()? – Ozzah

+0

@Ozzah: Encore une fois, vous avez juste besoin de copier les paramètres à un temporaire. De cette façon, il se ferme sur le temporaire (pas votre variable réelle), et obtiendra les bonnes valeurs. –

+0

@Ozzah: J'ai édité ma réponse pour montrer comment travailler autour du cas 2 .. –

3

Votre problème est le même; ce n'est pas la syntaxe lambda elle-même, c'est le fait que vous fermez une variable locale dans une méthode anonyme (la syntaxe delegate que vous utilisez était la première itération des méthodes anonymes, qui a fait ses débuts dans .NET 2.0).

Si vous voulez faire cela, vous aurez vous utilisez une solution de contournement:

for (int i = 0; i < 5; i++) 
{ 
    int j = i; 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + j); 
    }))).Start(); 
} 

Notez que ceci est semblable à ce que vous avez essayé (copie), mais il doit être en dehors du fermeture et à l'intérieur la boucle. La copier dans la fonction anonyme (comme dans votre exemple) n'aide pas.

Questions connexes