2009-08-20 5 views
1

Je rencontre un peu de difficulté en utilisant la classe threadpool et un tableau de ManualResetEvents. Voici un exemple simple de ce que je fais. Le problème est que dans la méthode DoWork j'obtiens des références nulles à l'objet resetEvent [param as int].C# Threadpool Synchronization Inquiry

Je n'arrive pas à comprendre ce que je fais de mal.

(edit: a obtenu le fonctionnement du bloc de code)

private static volatile ManualResetEvent[] resetEvents = new ManualResetEvent[NumThreads]; 
public void UpdateServerData() 
{ 
    for (int i = 0; i < NumThreads ; i++) 
     { 
      resetEvents[i] = new ManualResetEvent(false); 
      ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), (object) i); 

     } 
    WaitHandle.WaitAll(resetEvents); 
} 
private void DoWork(object param) 
{ 
//do some random work 
resetEvents[(int)param].Set(); 
} 

EDIT: J'ai essayé d'insérer un System.Threading.Thread.MemoryBarrier(); après chaque .Set() cependant j'obtiens toujours une exception de référence nulle.

+0

J'ai effectué plusieurs modifications pour que le bloc de code fonctionne. Si quelqu'un a vu les itérations précédentes, excusez le gâchis que c'était. – Setheron

+0

J'ai également essayé de verrouiller chaque appel .Set() parce que cela devrait provoquer une lecture/écriture volatile de l'objet mais cela ne semble pas fonctionner non plus. Très frustrant. – Setheron

+0

Pourquoi diable établissez-vous une barrière de mémoire * après * le 'Set()'? Vous devez voir l'élément de tableau mis à jour * avant * que vous appelez 'Set()' dessus! –

Répondre

0

Quasiment je trouve la question était

for (int i = 0; i < NumThreads ; i++)  
{   
resetEvents[i] = new ManualResetEvent(false);   
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), resetEvents[i]);  
} 

Au lieu de déclarer une nouvelle ManualResetEvent J'ai simplement appelé Reset(). Le problème semble avoir été que, bien que j'utiliserais MemoryBarrier ou des verrous, la mémoire physique ne serait pas encore mise à jour, donc elle pointerait sur null.

2

Vous ne pouvez pas utiliser le mot clé as pour convertir en int (puisque int n'est pas un type de référence). Utilisez (int)param à la place:

private void DoWork(object param) 
{ 
    //do some random work 
    resetEvents[(int)param].Set(); 
} 

Une autre approche que je me sens est plus propre est de passer la poignée d'attente à la méthode à la place:

public void UpdateServerData() 
{ 
    for (int i = 0; i < NumThreads ; i++) 
    { 
     resetEvents[i] = new ManualResetEvent(false); 
     ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), resetEvents[i]); 
    } 
    WaitHandle.WaitAll(resetEvents); 
} 
private void DoWork(object param) 
{ 
    //do some random work 
    (param as ManualResetEvent).Set(); 
} 

De cette façon, la méthode de travail n'a aucune connaissance sur la façon dont la poignée d'attente est géré à l'extérieur; et il ne peut pas non plus atteindre les poignées d'attente pour d'autres threads par erreur.

+0

Je viens d'écrire rapidement un exemple simple. Je ne voulais pas pour être 100% syntaxiquement correct mais je vais corriger l'exemple – Setheron

+0

ne pense pas que vous vouliez utiliser le param comme l'indexeur .... devrait être en mesure de faire (param comme ManualResetEvent) .Set() – CSharpAtl

+0

@CSharpAtl: oui, j'ai remarqué ça. Mon code est mort le copier/coller la mort, je suppose; o) –

1

volatile ManualResetEvent[] ne signifie pas que l'accès à éléments de tableau suit la sémantique volatile. Seul l'accès à la variable contenant la référence au tableau sera volatile. Essayez d'insérer une barrière de mémoire après avoir affecté l'élément de tableau, ou en utilisant Thread.VolatileWrite pour les définir, par ex.

Thread.VolatileWrite (ref resetEvents[i], new ManualResetEvent (false)) ; 
+0

Oh. Je pensais que l'affectation de volatiles au tableau faisait que les membres héritaient du mot-clé. Je ne suis pas familier avec les deux autres façons dont vous avez parlé (barrière de mémoire et Interlocked.Exchange) – Setheron

+0

En fait, Interlocked.Xxx n'est pas une bonne idée ici, car vous n'avez pas besoin de lire et mettre à jour vos données. –