2009-08-19 11 views
0

Je ne sais pas si le titre a du sens, mais dans l'application que j'écris il y a beaucoup de méthodes (d'extension). Un exemple simple:Mise en œuvre automatique de la boucle

Objets:

Matter (Burn, Explode, Destroy, Freeze, Heat, Cool) 
Atom (Attach, Detach) 
<many more> 

Et une collection personnalisée comme:

ImmutableList<T> 

et des méthodes comme celles-ci:

public static class Burner 
{ 
    public static Matter Burn (this Matter matter) 
    { 
     // matter is burning ... 
    } 
} 

var matters = new ImmutableList<Matter>(); 
matters.Burn(); 

Comme vous pouvez le voir, Brûler des œuvres sur une seule entité, mais apparaît toujours sur ImmutableList. De cette façon, je veux gérer la paralellisation (brûler en parallèle) moi-même.

Comment procéder de la manière la plus performante, la plus propre, la plus facile à entretenir ou la combinaison? Premièrement, je préfère ne pas définir une autre méthode d'extension qui prend ImmutableList à l'intérieur de chaque classe (Burner, etc), car il y en a des centaines sur des centaines comme celles-ci et elles vont probablement se ressembler. Mais je suis ouvert aux idées.

Tout le code est à moi, donc je peux changer/ajouter n'importe quoi dans n'importe quelle partie du code, pas seulement les méthodes d'extension.

Répondre

2

Quel est le problème avec

matters.ForEach(Burner.Burn); 

avec votre propre implémentation de ForEach?

+0

Merci, mais ForEach n'est pas parallèle, et je veux gérer ma propre parallélisation aux besoins spécifiques de l'application. Aussi je préférerais la syntaxe dans mon post si possible. –

+0

Peut-être que je ne comprends pas votre problème correctement, mais qu'est-ce qui vous empêche d'implémenter votre propre méthode 'ForEach'? – dtb

+0

Je pourrais, mais je voudrais avoir une syntaxe comme: questions.Burn(); Sinon, il y aura beaucoup d'appels ForEach dans le code, pas que ce soit une mauvaise chose, mais c'est tellement évident que je veux un appel "direct", donc des méthodes d'extension. –

0

créer votre propre extension ForEachParallel alors si vous ne voulez pas utiliser PLINQ ou quelque chose

1

Voici une classe simple qui itère de façon parallèle.

Emre Aydinceren

Utilisation:

Parallel.ForEach (questions, matière => matter.Burn());

ou

matters.ParallelForEach (matière => matter.Burn());

/// <summary> 
/// Provides concurrent processing on a sequence of elements 
/// </summary> 
public static class Parallel 
{ 
    /// <summary> 
    /// Number Of parallel tasks 
    /// </summary> 
    public static int NumberOfParallelTasks; 


    static Parallel() 
    { 
     NumberOfParallelTasks = Environment.ProcessorCount < 65 ? Environment.ProcessorCount : 64; 
    } 

    /// <summary> 
    /// Performs the specified action on each element of the sequence in seperate threads. 
    /// </summary> 
    /// <typeparam name="T">The type of the elements of source.</typeparam> 
    /// <param name="source">A sequence that contains elements to perform action</param> 
    /// <param name="action">The Action delegate to perform on each element of the IEnumerable.</param> 
    public static void ForEach<T>(IEnumerable<T> source, Action<T> action) 
    { 
     if(source == null) return; 

     //create a new stack for parallel task we want to run , stack is very performant to add and read elements in sequence 
     var stacks = new Stack<T>[NumberOfParallelTasks]; 

     //instantiate stacks 
     for(var i = 0;i < NumberOfParallelTasks;i++) 
     { 
      stacks[i] = new Stack<T>(); 
     } 

     var itemCount = 0; 

     //spread items in source to all stacks while alternating between stacks 
     foreach(var item in source) 
     { 
      stacks[itemCount++ % NumberOfParallelTasks].Push(item); 
     } 

     if(itemCount==0)return; 

     //if we have less items than number of Parallel tasks we should only spun threads for active stacks 
     var activeStackCount = itemCount < NumberOfParallelTasks ? itemCount : NumberOfParallelTasks; 

     //events are used to notify thread pool completed 
     var events = new ManualResetEvent[activeStackCount]; 

     for(var index = 0;index < activeStackCount;index++) 
     { 
      //assign index to a scope variable otherwise in multithreading index will not be consistant 
      var listIndex = index; 

      events[listIndex] = new ManualResetEvent(false); 

      ThreadPool.QueueUserWorkItem(delegate 
      { 
       //name the thread for debugging 
       if(String.IsNullOrEmpty(Thread.CurrentThread.Name)) 
       { 
        Thread.CurrentThread.Name = String.Format("Parallel.ForEach Worker Thread No:{0}", listIndex); 
       } 

       try 
       { 
        //iterate through our stack 
        var stack = stacks[listIndex]; 
        foreach(var item in stack) 
        { 
         action(item); 
        } 
       } 
       finally 
       { 
        //fire the event to signal WaitHandle that our thread is completed 
        events[listIndex].Set(); 
       } 

      }); 
     } 

     WaitAll(events); 

    } 

    private static void WaitAll(WaitHandle[] waitHandles) 
    { 
     if(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) 
     { 
      for(var index = 0;index < waitHandles.Length;index++) WaitHandle.WaitAny(waitHandles); 
     } 
     else 
     { 
      WaitHandle.WaitAll(waitHandles); 
     } 
    } 

    /// <summary> 
    /// Performs the specified action on each element of the sequence in seperate threads. 
    /// </summary> 
    /// <typeparam name="T">The type of the elements of source.</typeparam> 
    /// <param name="source">A sequence that contains elements to perform action</param> 
    /// <param name="action">The Action delegate to perform on each element of the IEnumerable.</param> 
    public static void ParallelForEach<T>(this IEnumerable<T> source, Action<T> action) 
    { 
     ForEach(source, action); 
    } 

} 
+0

Il ne devrait pas y avoir une seule pile compatible avec la concurrence tâches et un pool de threads. Lorsque chaque thread démarre, il vérifie la pile pour le travail. Si la pile est vide, elle se ferme. De cette façon, certains travaux sont longs et d'autres sont courts, vous ne vous retrouvez pas avec un thread exécutant 10 jobs rapides et sortant et d'autres exécutant 10 jobs lents tout seul. – jmucchiello

2

Vous pouvez trouver this article être une lecture intéressante. Il explique comment un foreach parallèle peut fonctionner, à la fois le faire vous-même et en utilisant le Parallel extensions CTP pour .NET 3.5. Avec le CTP, vous pouvez le faire (exemple tiré de l'article ci-dessus):

using System.Threading; 

// A simple string collection 
string[] numbers = { "One", "Two", "Three", "Four", "Five", "Six", "Seven", 
    "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen"}; 

// equivalent to: foreach (string n in numbers) 
Parallel.ForEach<string>(numbers, delegate(string n) 
{ 
    Console.WriteLine("n={0}", n.ToString()); 
}); 

Vous devriez hésiter à utiliser un CTP dans le code de production, à moins qu'il est juste pour vos propres projets (dans ce cas, vous devriez probablement envie essayez les CTP).