2009-09-14 2 views
1

Je viens de commencer à jouer avec du filetage aujourd'hui et j'ai rencontré quelque chose que je ne comprends pas.Lancer un thread à partir d'une boucle et passer l'ID de boucle

public void Main() 
{ 
    int maxValue = 5; 
    for (int ID = 0; ID < maxValue; ID++) 
    { 
     temp(ID); 
    } 
} 

public void temp(int i) 
{ 
    MessageBox.Show(i.ToString()); 
} 

Comme base qu'il obtient qui fonctionne très bien, mais lorsque je tente de créer un nouveau fil pour chacun, il ne passe que valeurmax. S'il vous plaît ne tenez pas compte de la gravité de cette situation, je l'ai seulement écrit comme un exemple simpliste.

public void Main() 
{ 
    int maxValue = 5; 
    for (int ID = 0; ID < maxValue; ID++) 
    { 
     threads.Add(new Thread(() => temp(myString, rowID))); 
     threads[rowID].Start(); 
    } 
} 

public void temp(string myString, int i) 
{ 
    string _myString = myString; 

    MessageBox.Show(i.ToString()); 
} 

Compte tenu de cela, j'ai deux questions: 1) Pourquoi la méthode ne marche pas un se fait appel à un nouveau thread qui passe l'ID? 2) Comment devrait-il être correctement codé?

+0

bien d'abord, vous avez une variable rowID non déclarée. Renommez les deux occurrences en ID et cela fonctionnera ALL DROIT. La réponse ci-dessous est OBSOLÈTE! – mnn

+0

rowID est une faute de frappe .. si cela est changé en ID, cela ne fonctionne pas. La réponse de Jons est correcte. –

+0

@mnn: Non, ce ne sera pas le cas. À quel point êtes-vous familier avec la manière dont les variables capturées fonctionnent en C#? –

Répondre

9

Le problème est que vous n'avez qu'une seule variable ID et qu'elle est en cours de capture. La variable est seulement read lorsque le code dans le nouveau thread est réellement exécuté, ce qui sera souvent après que votre thread principal a terminé sa boucle, laissant ID à maxValue. Prenez une copie de chaque itération de boucle, de sorte que vous capturez une variable différente à chaque fois:

for (int ID = 0; ID < maxValue; ID++) 
{ 
    int copy = ID; 
    threads.Add(new Thread(() => temp(myString, copy))); 
    threads[rowID].Start(); 
} 

Ceci est une erreur commune avec des fermetures. Lisez my article comparing C# and Java closures pour plus d'informations. La même chose se produit avec foreach, en passant - et qui est encore plus déroutant, car il lit comme vous avez une nouvelle à chaque fois que la variable:

foreach (string url in urls) 
{ 
    // Aargh, bug! Don't do this! 
    new Thread(() => Fetch(url)).Start(); 
} 

Encore une fois, vous finissez seulement avec une variable. Vous avez besoin de chaque délégué pour capturer une variable distincte, de sorte que vous utilisez à nouveau une copie:

foreach (string url in urls) 
{ 
    string urlCopy = url; 
    new Thread(() => Fetch(urlCopy)).Start(); 
} 
+0

Vous avez fait la même erreur que DataPimp. Vous n'avez pas besoin d'avoir une autre variable, renommez simplement rowID en ID et ça marchera bien. – mnn

+0

Merci beaucoup, je ne suis pas sûr de comprendre à 100% pourquoi le premier code que j'ai posté est exécuté mais le second ne le fait pas (je suppose que cela a du sens) mais je vais certainement lire cet article et j'espère . –

+1

@mnn: Non, ce ne sera pas le cas. Vous allez vous retrouver avec le problème de variable capturée. Ce n'est peut-être pas toujours visible, mais vous aurez certainement un bug. L'utilisation d'une variable de boucle capturée lors du démarrage de threads est terriblement sujette aux bogues. J'ai supposé que l'utilisation de 'rowID' était juste une faute de frappe, étant donné que l'utilisation d'une variable non déclarée est un bug * compile time * plutôt qu'un bug * execution time *. –