2009-04-30 3 views
1

Ich habe ein Bildbetrachtungs-Steuerelement für meinen Chef erstellt, das Schwenken, Zoomen über das Mausrad und Zeichnen einer Box zum Zoomen enthält. Die Steuerung muss sehr große Bilddateien unterstützen (d. H. Mehrere tausend Pixel auf jeder Seite).Verschachteltes Bildskalierungs-Sub-Bild bleibt unten UI

Alles funktioniert, aber wenn der Code das Bild skaliert, reagiert die Benutzeroberfläche der Steuerung nicht mehr. Mein Chef ließ mich den Skalierungscode mithilfe von Threading von der Benutzeroberfläche unterscheiden. Der Skalierungscode befindet sich nun definitiv in einem separaten Thread, aber die Benutzeroberfläche ist immer noch blockiert, während der Skalierungscode läuft! Kann mir bitte jemand dabei helfen ??

Unten ist der Skalierungscode. Lass es mich wissen, wenn das nicht genug ist, um mir zu helfen, und ich werde den Code posten, den du brauchst!

UPDATE: Hier ist der Kontrollcode in seiner Gesamtheit. link text

+1

Wie rufen Sie diese Methode auf? Ihr Problem besteht nicht in dieser Funktion, sondern darin, dass Sie versuchen, es asynchron auszuführen. Bitte posten/beschreiben Sie den aufrufenden Kontext. –

+0

Ski, bitte versuchen Sie den Code aus dem neuesten Update in meiner Antwort. Außerdem übergeben Sie sowohl die ScaleImageArguments als auch die img-Argumente ByVal. Diese sollten ByRef übergeben werden. Obwohl dies nicht der Grund ist, blockiert der Thread während der Ausführung von ScaleImage. –

Antwort

0

Es scheint, dass es eine globale Sperre in der GDI + API gibt. Für Test habe ich zwei Threads basierend auf folgende Funktion

 
static void test_thread() 
{ 
    Bitmap bmp = new Bitmap(4000, 4000); 
    Graphics g = Graphics.FromImage(bmp); 
    Brush b = Brushes.Red ; 

    for (; ;) { 
     g.FillRectangle(b, 0, 0, bmp.Width, bmp.Height); 
    } 
} 

Wenn die Endlos-Schleife hat ben leer gelassen, war die CPU-Auslastung über 90%, so dass sie sie beide Kerne meiner CPU genutzt. Wenn FillRectangle vorhanden ist, war die Verwendung geringfügig unter 50%, was darauf hinweist, dass nur ein Thread es gleichzeitig ausführen kann.

Es ist also möglich, dass jeder Aufruf von GDI +, den Sie während der Skalierung aus dem GUI-Thread ausführen, blockiert wird, bis die Skalierung abgeschlossen ist.

+0

Sie sind richtig! Ich habe versucht, jeden Code in der Thread-Funktion, die Graphics verwendet, durch eine lange laufende, leere for-Schleife zu ersetzen, und das Threading funktioniert wie beabsichtigt - mein Steuerelement zeigt jetzt eine leere Box ohne den Graphics-Code. Jetzt ist die Frage, wie kann ich diese GDI + Blockierung umgehen? Muss ich meine eigene Skalierungsfunktion schreiben? – Ski

+0

Sie könnten versuchen, die alte GDI mit dem Pinvoke-Mechanismus zu verwenden. Das Problem ist, dass Sie keine Kontrolle über die Qualität der Skalierung haben und es möglicherweise andere Einschränkungen gibt. Ansonsten glaube ich, dass Sie Ihren eigenen Scaler schreiben oder einen vorhandenen finden müssen. – Komat

0

Ich vermute, dass, da die Skalierung alle CPU verwendet, die die UI bogging. Beide Threads haben wahrscheinlich die gleiche Priorität.

Versuchen Sie, die Priorität des Skalierungs-Threads zu verringern, damit die Benutzeroberfläche reagieren kann.

Values for Thread Priority: 

Above Normal -> Gives thread higher priority 
Below Normal ->Gives thread lower priority 
Normal -> Gives thread normal priority 
Lowest -> Gives thread lowest priority 
Highest -> Gives thread highest priority 

so you would probably use: 

Thread1.Priority=System.Threading.ThreadPriority.BelowNormal 
+1

Ich bezweifle irgendwie, dass das der Fall ist. Ich denke, die Invokation ist wahrscheinlich das Problem. –

+0

In der Sub, die das obige Unterprogramm erstellt und startet, habe ich die Priorität bereits auf BelowNormal gesetzt. Es macht keinen Unterschied, wenn ich die Priorität auf Niedrigste setze. – Ski

+0

Ski, geben Sie bitte Details darüber an, wie Sie diese Methode asynchron aufrufen. So können wir helfen! –

0

Versuchen Sie es stattdessen.

Private Sub Worker(object o) 
    Dim args as ScaleImageArguments 
    args = CType(o, ScaleImageArguments) 
    ScaleImage(args) 
End Sub 

Private Sub RunScaleImageAsync(ByVal img As Image, ByVal scale As Double) 
    System.Threading.ThreadPool.QueueUserWorkItem(Worker, _ 
       New ScaleImageArguments(img.Clone, scale)) 
End Sub 

ALTERNATIVE - Mit dem Muster Async.

Private Delegate Sub ScaleImageDelegate(ByRef arg As ScaleImageArguments) 

Private Sub BeginScaleImage(ByRef img As Image, ByVal scale As Double) 
    Dim d As ScaleImageDelegate 
    d = New ScaleImageDelegate(AddressOf ScaleImage) 

    d.BeginInvoke(New ScaleImageArguments(img.Clone, scale), _ 
        New AsyncCallback(AddressOf EndScaleImage), d)   
End Sub 

Private Sub EndScaleImage(ar As IAsyncResult) 
    Dim d As ScaleImageDelegate 
    d = CType(ar.AsyncState, ScaleImageDelegate) 
    d.EndInvoke(ar) 
End Sub 

Dann rufen Sie einfach BeginScaleImage, um es asynchron auszuführen.

BEARBEITEN - Siehe Korrekturen oben. Das Argument ar auf der EndScaleImage sollte mit ByRef deklariert werden, und auch der Parameter img der BeginScaleImage. Es gibt keinen Grund, warum sie ByVal übergeben werden sollten !!

+0

Habe es einfach ausprobiert, leider hat es keinen Unterschied gemacht. – Ski

+0

Haben Sie auch die zweite Option ausprobiert? –

+0

Ich habe die zweite Option ausprobiert. Mit img als ByVal in BeginScaleImage gab es keinen Unterschied. Mit Img als ByRef wird der Code in einer Endlosschleife gefangen, während das Bild geladen wird. – Ski

0

Wie stellt Ihr ImageScaled() das Bild wieder auf der Benutzeroberfläche dar?

+0

Das Steuerelement hat ein Dictionary mit Bildern namens ScaledImageHash, das die Skalierung des Bildes (ein Double) als Schlüssel verwendet. Das Paint-Untermenü des Steuerelements zeichnet dann das Bild in ScaledImageHash, das dem aktuellen Bildmaßstab entspricht. Das Wörterbuch ermöglicht es mir, 1) zuvor skalierte Bilder für die zukünftige Verwendung zu speichern, und noch wichtiger 2) Gibt mir die Möglichkeit, das Bild basierend auf dem potenziellen zukünftigen Maßstab präsektiv skalieren, wenn der Benutzer mit dem Mausrad zoomt. Das funktioniert, aber jedes Mal, wenn ich das Bild zoome, skaliert das Steuerelement präventiv das Bild erneut und die UI wird blockiert. – Ski

+1

Konnte die Farbe stecken bleiben und warten? Sie sagen, Sie haben ein Wörterbuch, was in Ordnung ist, aber was zeigt die Benutzeroberfläche an, während Sie darauf warten, dass die Skalierung stattfindet? Wartet es im Farbfaden, bis die Skalierung abgeschlossen ist, oder kehrt es einfach zurück und NICHT, bis die Skalierung beendet ist? Wenn es das erste ist, auch wenn es in einem anderen Thread gemacht wird, ist es immer noch "seriell" und somit bringt das Threading nichts. Die zweite erfordert Ihren Rückruf, um es dann zu malen. –

+0

@Kevin können Sie Recht haben. Ich dachte, dass er das ImageScaled-Ereignis verwendet, um zu wissen, wann die Operation abgeschlossen ist - obwohl Ihr Szenario erklären würde, warum sein Threading nicht die erwarteten Ergebnisse liefert. –