2009-08-19 5 views
0

Ich weiß nicht, ob der Titel Sinn macht, aber in der App, die ich schreibe gibt es viele (Erweiterung) Methoden. Ein einfaches Beispiel:Automatische Implementierung Schleife

Objekte:

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

und eine eigene Sammlung wie:

ImmutableList<T> 

und Methoden wie diese:

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

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

Wie Sie sehen können, Brennen Arbeiten eine einzelne Entität, aber immer noch auf ImmutableList. Auf diese Weise möchte ich Paralellisierung (parallel brennen) selbst verwalten.

Wie mache ich das am leistungsfähigsten, am saubersten oder am wartungsfreundlichsten oder kombiniert?

Erstens würde ich lieber nicht eine andere Erweiterungsmethode definieren, die ImmutableList innerhalb jeder Klasse (Brenner, etc) nimmt, weil es Hunderte von Hunderten wie diesen gibt und sie werden wahrscheinlich gleich aussehen. Aber ich bin offen für Ideen.

Auch der gesamte Code gehört mir, also kann ich irgendetwas in jedem Teil des Codes ändern/hinzufügen, nicht nur die Erweiterungsmethoden.

Antwort

2

Was mit

matters.ForEach(Burner.Burn); 

mit Ihrer eigenen Implementierung von ForEach falsch?

+0

Danke, aber ForEach ist nicht parallel, und ich möchte meine eigene Parallelisierung auf die spezifischen Bedürfnisse der App verwalten. Auch würde ich lieber die Syntax in meinem Beitrag nach Möglichkeit verwenden. –

+0

Vielleicht verstehe ich Ihr Problem nicht richtig, aber was hindert Sie daran, Ihre eigene "ForEach" -Methode zu implementieren? – dtb

+0

könnte ich, aber ich hätte gerne eine Syntax wie: matters.Burn(); Sonst wird es viele ForEach-Aufrufe im Code geben, nicht, dass es eine schlechte Sache ist, aber es ist so offensichtlich, dass ich einen "direkten" Aufruf, also Erweiterungsmethoden, möchte. –

0

Ihre eigene ForEachParallel Erweiterung erstellen Sie dann, wenn Sie nicht wollen, verwenden PLINQ oder etwas

1

Hier ist eine einfache Klasse, die eine Iteration in einer parallelen Weise.

Emre Aydinceren

Verbrauch:

Parallel.ForEach (Angelegenheiten, Materie => matter.Burn());

oder

matters.ParallelForEach (Materie => 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

Sollte es nicht nur einen Concurrency bewusst Stapel von Aufgaben und ein Pool von Threads. Wenn jeder Thread startet, prüft er den Stapel auf Arbeit. Wenn der Stapel leer ist, wird er beendet. Auf diese Art und Weise sind einige Jobs lang und manche sind kurz, Sie enden nicht mit einem Thread, der 10 schnelle Jobs ausführt und andere 10 langsame Jobs alleine ausführt. – jmucchiello

2

Sie können this article finden eine interessante Lektüre. Es wird erläutert, wie eine parallele foreach funktionieren kann, und zwar sowohl mit sich selbst als auch mit der Parallel extensions CTP für .NET 3.5. Mit der CTP können Sie dieses (Beispiel aus dem Artikel entnommen oben) tun:

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()); 
}); 

Sie sollten zögern, eine CTP in Produktionscode zu verwenden, es sei denn, es nur für Ihre eigenen Projekte ist (in diesem Fall sollten Sie wahrscheinlich wollen versuchen Sie CTPs).

Verwandte Themen