2012-10-26 8 views
5

Ok, hier ist die Situation: Mein Haupt/UI-Thread (nennen wir es Thread1) wird verwendet, um einen Stapel von Bildern von einem physischen Dokumentenscanner zu erhalten. Wenn ein Stapel erfasst wurde, wird ein separater "Hintergrund" -Thread (nennen Sie ihn Thread2) gestartet, um die Bilder von diesem Stapel zu verarbeiten und zu speichern.Priorität für Parallel.For einstellen loop

Thread2 (der "Hintergrund" -Thread) verwendet eine Parallel.For-Schleife, die die Bildverarbeitungs-/Speicherzeit um 70% gegenüber einer normalen For-Schleife reduziert. Es scheint jedoch auch, alle meine Prozessoren zu maximieren, so dass Thread1 nicht mehr Bilder aufnehmen kann, bis die Parallel.For Schleife abgeschlossen ist.

Gibt es eine Möglichkeit, eine Parallel.For Schleife zu "begrenzen", so dass es meine Prozessoren nicht maximal macht? Oder um die Verarbeitungspriorität festzulegen? Ich habe versucht, Thread2.Priority = ThreadPriority.Lowest Einstellung, aber dies scheint nicht auf die Schleife auswirken. Oder verstehe ich nicht, wie eine Parallel.For Schleife funktioniert? Blockiert es Thread1 irgendwie?

Hier ist wie ich den Thread2 von einer Methode in Thread1 aufrufen.

public void SaveWithSettings(bool save) // method in Thread1 
{ 
    .... 
    Thread thr = new Thread(ThreadWork); // creating new thread (Thread 2) 
    thr.Priority = ThreadPriority.Lowest; // does nothing? 
    thr.Start(new SaveContainer(sc)); // pass a copy as paramater 

    // misc stuff to make scanning possible again 
    numBgw++; 
    twain.RemoveAllImages(); // clear images 
    imagelist.Clear(); // clear imagelist images 
    .... // etc. this all appears to process fine while Thread2 is processing 
} 

Hier ist meine ThreadWork Methode:

private void ThreadWork(object data) // executing in Thread2 
{ 
    SaveContainer sc = data as SaveContainer; // holds images 

    bool[] blankIndex = new bool[sc.imagelist.Count]; // to use in Parallel.For loop 
    for (int i = 0; i < sc.imagelist.Count; i++) 
     blankIndex[i] = false; // set default value to false (not blank) 

    Parallel.For(0, sc.imagelist.Count, i => // loop to mark blank images 
    { 
     bool x = false; // local vars make loop more efficient 
     x = sc.IsBlankImage((short)i); // check if image at index i is blank 
     blankIndex[i] = x; // set if image is blank 
    } 
    .... // other image processing steps 
} 
+0

Sind Sie sicher, dass Thread 1 auf Gewinde 2 oder etwas nicht blockiert warten? Es wäre hilfreich, den Code zu sehen, was Thread 1 beim Warten macht. –

+4

Bitte zeigen Sie uns Ihren Code. – SLaks

+0

Das Festlegen der Priorität von thread2 wird keinen Unterschied machen, da Parallel.For auf Thread-Pool-Threads ausgeführt wird. –

Antwort

0

OK, ich habe es herausgefunden! Ich poste das nur für den Fall, dass jemand jemals versehentlich mit ihnen passiert ...

Es stellt sich heraus, dass der Parallel.For Thread nicht blockiert Thread1 (ja, Sie waren in Ordnung). Ein Objekt in Thread1 versuchte jedoch, eine neue Thread aus der ThreadPool zu schnappen, während der Loop knirschte und somit die "Verzögerung" auftrat. Ich verwende ein Drittanbieter-SDK, das es mir ermöglicht, mit der TWAIN-Oberfläche zu interagieren, und es gab eine Option ScanInNewThread = true, die versuchte, jedes Mal einen neuen Thread zu erfassen, wenn der Benutzer einen neuen Scan startete (was während des Schleifens auftrat) . Ich konnte dies ändern, so dass ein einzelner (aber immer noch separater) Thread während einer gesamten Anwendungssitzung verwendet wird, anstatt einen neuen Thread für jeden Scan-Batch zu erfassen, und BANG, keine merkliche Verzögerung mehr.

SO - die Moral der Geschichte:

Bestehende Fäden noch funktionieren sollte „normal“ (mit Ausnahme des Gewindes Aufruf der Parallel.For loop), solange sie nicht mehr Threads aus dem ThreadPool zu greifen versuchen, während die Schleife läuft.

2

Gibt es eine Möglichkeit, "Grenze" eine Parallel.For Schleife, so dass es nicht max tut meine Prozessoren aus?

Ja, Sie können Optionen mit MaxDegreeOfParallelism = N hinzufügen.

Oder um die Verarbeitungspriorität festzulegen?

Nein. Es ist ein ThreadPool (geliehen) -Thread. Ändern Sie nicht seine Eigenschaften. Eigentlich ist es ein Haufen Pool-Threads.

Oder missverstehe ich, wie eine Parallel.For-Schleife funktioniert? Blockiert es Thread1 irgendwie?

Ja, von außen Parallel.For(...) ist ein blockierender Anruf. Führen Sie es also auf einer separaten Task oder einem Hintergrundarbeiter aus, nicht aus dem Hauptthread.

+0

Ich führe die Parallel.For-Schleife in Thread2, das ist mein "Hintergrund" -Thread. Meiner Meinung nach sollte dies Thread1 nicht blockieren, es sei denn, die Methode in Thread1, die Thread2 aufruft, wartet darauf, dass Thread2 beendet wird. – fancypants

2

Eine grobe Art wäre das Flag MaxDegreeOfParallelism in ParallelOptions.

var Options = new ParallelOptions(); 

// Keep one core/CPU free... 
Options.MaxDegreeOfParallelism = Environment.ProcessorCount - 1; 

Paralle.For(0, sc.imagelist.Count, Options, i => // loop to mark blank images 
{ 
    bool x = false; // local vars make loop more efficient 
    x = sc.IsBlankImage((short)i); // check if image at index i is blank 
    blankIndex[i] = x; // set if image is blank 
} 
0

Ohne Zugriff auf die gesamte Anwendung ist es schwierig, genau zu wissen, was hier geschieht, aber lassen Sie uns beginnen, indem Parallel.For Abbau:

private void ThreadWork(object data) // executing in Thread2 
{ 
    // Thread2 running here 
    SaveContainer sc = data as SaveContainer; // holds images 

    bool[] blankIndex = new bool[sc.imagelist.Count]; // to use in Parallel.For loop 
    for (int i = 0; i < sc.imagelist.Count; i++) 
     blankIndex[i] = false; // set default value to false (not blank) 

    // Thread2 blocks on this call 
    Paralle.For(0, sc.imagelist.Count, i => // loop to mark blank images 
    { 
     // Thread from the pool is running here (NOT Thread2)!!! 

     bool x = false; // local vars make loop more efficient 
     x = sc.IsBlankImage((short)i); // check if image at index i is blank 
     blankIndex[i] = x; // set if image is blank 
    } 
    // Thread2 resumes running here 

    .... // other image processing steps 
} 

Ändern So Thread2 Priorität wird keinen Unterschied machen, da es ist sowieso blockiert. Wenn Thread1 jedoch nicht blockiert ist, sollte es dennoch ausgeführt werden können. Thread1 wird möglicherweise nicht oft ausgeführt, was Ihr Problem sein könnte.

Der naive Ansatz wäre so etwas wie Chaos mit Thread-Prioritäten, zählt, oder fügen Sie einige Thread.Yield() -Anweisungen. Die Threads aus dem Pool blockieren jedoch wahrscheinlich bereits, da sie E/A ausführen.

Wahrscheinlich müssen Sie hier Ihren Code so umgestalten, dass Ihre Bildladeschleife das Bild Ihres Hauptthreads blockiert, indem Sie etwas wie System.Threading.WaitHandle verwenden oder mehr von der Arbeit des Hauptthreads verschieben das Bild wird geladen. Ohne Refactoring erhalten Sie nach der Erfahrung eine Lösung, die auf die spezifische Maschine zugeschnitten ist, unter der Sie testen, unter bestimmten Betriebsbedingungen, aber wenn sich die Lasten ändern oder sich die Hardware ändert, ist Ihr "Tuning" deaktiviert.

Überarbeiten Sie den Code, so dass mehr Arbeit in Ihrem Parallel.Für Arbeiter getan wird und blockieren Sie Ihre Threads auf Haupt-Thread-Aktivität, wenn es Arbeit für den Haupt-Thread gibt und Sie eine Lösung haben, auf die Sie stolz sind.

4
public static void PriorityParallelForeach<T>(this IEnumerable<T> source, Action<T> action, ThreadPriority threadPriority, int? maxDegreeOfParallelism = null) 
    { 
     if (maxDegreeOfParallelism == null || maxDegreeOfParallelism<1) 
     { 
      maxDegreeOfParallelism = Environment.ProcessorCount; 
     } 

     var blockingQueue = new BlockingCollection<T>(new ConcurrentQueue<T>(source)); 
     blockingQueue.CompleteAdding(); 

     var tasks = new List<Task>() ; 

     for (int i = 0; i < maxDegreeOfParallelism; i++) 
     { 
      tasks.Add(Task.Factory.StartNew(() => 
      { 
       while (!blockingQueue.IsCompleted) 
       { 
        T item; 
        try 
        { 
         item = blockingQueue.Take(); 
        } 
        catch (InvalidOperationException) 
        { 
         // collection was already empty 
         break; 
        } 

        action(item); 
       } 
      }, CancellationToken.None, 
        TaskCreationOptions.None, 
        new PriorityScheduler(threadPriority))); 
     } 

     Task.WaitAll(tasks.ToArray()); 

    } 

Oder einfach:

Parallel.ForEach(testList, item => 
      { 

       var priviousePrio = Thread.CurrentThread.Priority; 
       // Set your desired priority 
       Thread.CurrentThread.Priority = ThreadPriority.Lowest; 

       TestCalc(item); 

       //Reset priviouse priority of the TPL Thread 
       Thread.CurrentThread.Priority = priviousePrio; 
      });