none
Zusätzliche Informationen: Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet. RRS feed

  • Frage

  •  

    Ich möch

    te gern mit meiner Anwendung Faktale berechnen. Da dies ja etwas rechenintensiver ist, wollte ich die Berechnung in einen 2. Thread auslagern.

        class GenerateMandelbrotfractal
        {
            private int width;
            private int height;
            private double a;
            private double b;
    
            public GenerateMandelbrotfractal(int width, int height, double a, double b)
            {
                this.width = width;
                this.height = height;
                this.a = a;
                this.b = b;
            }
    
            public delegate void FinishedCreateNewDelegate(object sender, MandelbrotfractalEventArgs e);
            public event FinishedCreateNewDelegate FinishedCreateNew;
    
            public void Create()
            {<br/>            Bitmap bmp;<br/>            //Berechnung            <br/>            FinishedCreateNew(this, new MandelbrotfractalEventArgs(bmp));<br/>        }
    
                FinishedCreateNew(this, new MandelbrotfractalEventArgs(bmp));
            }
        }
    

            // Wird ausgeführt wenn der Einstellungsdialog ein Event auslöst.
            private void CreateMandelbrotfractalAsync(int width, int height, double a, double b)
            {
                GenerateMandelbrotfractal mbf = new GenerateMandelbrotfractal(width, height, a, b);
                mbf.FinishedCreateNew +=new GenerateMandelbrotfractal.FinishedCreateNewDelegate(mbf_FinishedCreateNew);
                ThreadStart tStart = new ThreadStart(mbf.Create);
                Thread t = new Thread(tStart);
                t.Priority = ThreadPriority.Highest;
                t.Start();
            }
    

            public void mbf_FinishedCreateNew(object sender, MandelbrotfractalEventArgs e)
            {
                BitmapImage img = new BitmapImage();
                MemoryStream ms = new MemoryStream();
                Bitmap bitmap = e.Bitmap;
                bitmap.Save(ms, ImageFormat.Png);
                ms.Position = 0;
                img = new BitmapImage();
                img.BeginInit();
                img.StreamSource = ms;
                img.EndInit();
                imageViewer.Source = img; // Zusätzliche Informationen: Der aufrufende Thread kann <br/>                                                        // nicht auf dieses Objekt zugreifen, da sich das Objekt im <br/>                                                        // Besitz eines anderen Threads befindet.
            }
    

            <Image Margin="3,9,3,3" Name="imageViewer" Stretch="None" />
    
    So und warum Funktioniert das jetzt nicht? der Event-Handler ist doch eigentlich zugriffsberechtigt das Bild zuzuweisen, oder?
    Koopakiller - http://koopakiller.ko.ohost.de/
    Samstag, 14. Januar 2012 21:31
    Moderator

Antworten

  • Hallo,

    grundsätzlich sind unter WPF (wie auch Windows Forms) Steuerelemente an den Thread gebunden, der sie erstellt ist.
    Die Ausnahme sind Freezables unter anderen das BitmapImage, die Du mit Freeze
    für Änderungen sperren kannst und dann threadübergreifend weitergeben kannst.

    Zudem musst Du auf den ImageViewer über den Dispatcher zugreifen, gerade heute beantwortet in:
    Wie nutzt man BeginInvoke in der WPF?

    Nebenbei:
    Anstatt einen zusätzlichen Delegaten kannst Du ein EventHandler<T> verwenden.
    Eine Task<T> aus der TPL kann ähnliches leisten wie ein eigener Thread (und auf Highest verzichte besser gleich)
    und noch mehr Cores nutzen (so denn vorhanden).
    Ein Mandelbrot Beispiel findest Du u. a. in Samples for Parallel Programming with the .NET Framework
    (und wenn es nur zum Vergleich ist, wer es schneller hinkriegt ;-))

    Gruß Elmar

    Samstag, 14. Januar 2012 22:51
  • Hallo,

    mit dem Ausfüllen der Lücken durch ein wenig Fiktion:

            private void CreateMandelbrotfractalAsync(int width, int height, double a, double b)
            {
                GenerateMandelbrotfractal mbf = new GenerateMandelbrotfractal(width, height, a, b);
                mbf.FinishedCreateNew += mbf_FinishedCreateNew;
    
                // new Thread(mbf.Create).Start();
    
                Task.Factory.StartNew(mbf.Create, TaskCreationOptions.LongRunning);
            }
    
            private void mbf_FinishedCreateNew(object sender, MandelbrotfractalEventArgs e)
            {
                this.Dispatcher.Invoke(new Action<Image>(
                    imageViewer => imageViewer.Source = e.Image),
                    this.imageViewer);
            }
    
    

        class GenerateMandelbrotfractal
        {
            private int width;
            private int height;
            private double a;
            private double b;
    
            public GenerateMandelbrotfractal(int width, int height, double a, double b)
            {
                this.width = width;
                this.height = height;
                this.a = a;
                this.b = b;
            }
    
            public event EventHandler<MandelbrotfractalEventArgs> FinishedCreateNew;
    
            public void Create()
            {
                PixelFormat pf = PixelFormats.Bgr32;
                int width = this.width;
                int height = this.height;
                int rawStride = (width * pf.BitsPerPixel + 7) / 8;
                byte[] rawImage = new byte[rawStride * height];
    
                Random value = new Random();
                value.NextBytes(rawImage);
    
                BitmapSource source = BitmapSource.Create(width, height, 96, 96, pf, null, rawImage, rawStride);
                
                // Einfrieren...
                source.Freeze();
                
                FinishedCreateNew(this, new MandelbrotfractalEventArgs(source));
            }
        }

    was zwar keine Mandelbrötchen liefert, aber zumindest zeigen sollte wie es auch geht ;-)

    Gruß Elmar


    Sonntag, 15. Januar 2012 09:10
  • Hallo,

    im Beispiel fehlt jede Fehlerbehandlung, die vermutlich eine OutOfMemoryException zu Tage bringen würde.

    Leider hatte ich fehlerhaften Code aus Byte-Array in Image umwandeln kopiert,
    und zwar die fehlerhafte Formel für rawStride, die heissen muss:

    int rawStride = (width * pf.BitsPerPixel + 7) / 8;
    

    was auch deutlichen Effekt auf den benötigten Speicher hat.

    Wobei generell gilt:
    Hast Du nicht genug (zusammenhängenden) Speicher ist das Programm am Ende -
    bei so etwas kann x64 (64 Bit) kann schon hilfreich sein.

    Dein ursprünglicher Code wiederum könnte das Problem verschärfen,
    da ein MemoryStream  ein (Byte)-Array verwendet und die Drawing.Bitmap auch.

    Je nachdem, wo Dein Mandelbrot Generator so herkommt,
    empfiehlt es sich ihn auf eine BitmapSource umzuschreiben.

    Gruß Elmar

    Sonntag, 15. Januar 2012 12:32

Alle Antworten

  • Hallo,

    grundsätzlich sind unter WPF (wie auch Windows Forms) Steuerelemente an den Thread gebunden, der sie erstellt ist.
    Die Ausnahme sind Freezables unter anderen das BitmapImage, die Du mit Freeze
    für Änderungen sperren kannst und dann threadübergreifend weitergeben kannst.

    Zudem musst Du auf den ImageViewer über den Dispatcher zugreifen, gerade heute beantwortet in:
    Wie nutzt man BeginInvoke in der WPF?

    Nebenbei:
    Anstatt einen zusätzlichen Delegaten kannst Du ein EventHandler<T> verwenden.
    Eine Task<T> aus der TPL kann ähnliches leisten wie ein eigener Thread (und auf Highest verzichte besser gleich)
    und noch mehr Cores nutzen (so denn vorhanden).
    Ein Mandelbrot Beispiel findest Du u. a. in Samples for Parallel Programming with the .NET Framework
    (und wenn es nur zum Vergleich ist, wer es schneller hinkriegt ;-))

    Gruß Elmar

    Samstag, 14. Januar 2012 22:51
  • OK, wo das Problem liegt weiß ich schonmal.

    Allerdings weiß ich noch nicht so recht wie ich Das Problem löse.

                this.Dispatcher.Invoke(new Action<System.Windows.Controls.Image>(
                    imageViewer => imageViewer.Source = img),
                    this.imageViewer);
    


    Fehler:
       Eine nicht behandelte Ausnahme des Typs "System.Reflection.TargetInvocationException" ist in mscorlib.dll aufgetreten.
       Zusätzliche Informationen: Ein Aufrufziel hat einen Ausnahmefehler verursacht.

    Task<T> und EventHandler<T> versteh ich auch nicht so richtig wie ich es müsste.
    Was mach ich falsch?

     

    Zum Mandelbrot: Danke für die Seite, die Zoomfunktion ist eine gute Idee.

     


    Koopakiller - http://koopakiller.ko.ohost.de/
    Samstag, 14. Januar 2012 23:56
    Moderator
  • Hallo,

    mit dem Ausfüllen der Lücken durch ein wenig Fiktion:

            private void CreateMandelbrotfractalAsync(int width, int height, double a, double b)
            {
                GenerateMandelbrotfractal mbf = new GenerateMandelbrotfractal(width, height, a, b);
                mbf.FinishedCreateNew += mbf_FinishedCreateNew;
    
                // new Thread(mbf.Create).Start();
    
                Task.Factory.StartNew(mbf.Create, TaskCreationOptions.LongRunning);
            }
    
            private void mbf_FinishedCreateNew(object sender, MandelbrotfractalEventArgs e)
            {
                this.Dispatcher.Invoke(new Action<Image>(
                    imageViewer => imageViewer.Source = e.Image),
                    this.imageViewer);
            }
    
    

        class GenerateMandelbrotfractal
        {
            private int width;
            private int height;
            private double a;
            private double b;
    
            public GenerateMandelbrotfractal(int width, int height, double a, double b)
            {
                this.width = width;
                this.height = height;
                this.a = a;
                this.b = b;
            }
    
            public event EventHandler<MandelbrotfractalEventArgs> FinishedCreateNew;
    
            public void Create()
            {
                PixelFormat pf = PixelFormats.Bgr32;
                int width = this.width;
                int height = this.height;
                int rawStride = (width * pf.BitsPerPixel + 7) / 8;
                byte[] rawImage = new byte[rawStride * height];
    
                Random value = new Random();
                value.NextBytes(rawImage);
    
                BitmapSource source = BitmapSource.Create(width, height, 96, 96, pf, null, rawImage, rawStride);
                
                // Einfrieren...
                source.Freeze();
                
                FinishedCreateNew(this, new MandelbrotfractalEventArgs(source));
            }
        }

    was zwar keine Mandelbrötchen liefert, aber zumindest zeigen sollte wie es auch geht ;-)

    Gruß Elmar


    Sonntag, 15. Januar 2012 09:10
  • OK, dein Code funktioniert. Nur noch eine Frage: Ich wollte ein "Rauschen" von 500*500 Pxel erstellen. Dabei hat der Debugger den Vorgang einfach ohne Meldung bei dieser Zeile abgebrochen:
                byte[] rawImage = new byte[rawStride * height];
    

    Ist das für Windows zu viel Arbeitsspeicher, oder was?
    Koopakiller - http://koopakiller.ko.ohost.de/
    Sonntag, 15. Januar 2012 10:55
    Moderator
  • Hallo,

    im Beispiel fehlt jede Fehlerbehandlung, die vermutlich eine OutOfMemoryException zu Tage bringen würde.

    Leider hatte ich fehlerhaften Code aus Byte-Array in Image umwandeln kopiert,
    und zwar die fehlerhafte Formel für rawStride, die heissen muss:

    int rawStride = (width * pf.BitsPerPixel + 7) / 8;
    

    was auch deutlichen Effekt auf den benötigten Speicher hat.

    Wobei generell gilt:
    Hast Du nicht genug (zusammenhängenden) Speicher ist das Programm am Ende -
    bei so etwas kann x64 (64 Bit) kann schon hilfreich sein.

    Dein ursprünglicher Code wiederum könnte das Problem verschärfen,
    da ein MemoryStream  ein (Byte)-Array verwendet und die Drawing.Bitmap auch.

    Je nachdem, wo Dein Mandelbrot Generator so herkommt,
    empfiehlt es sich ihn auf eine BitmapSource umzuschreiben.

    Gruß Elmar

    Sonntag, 15. Januar 2012 12:32
  • Hallo Koopakiller,

    Ich gehe davon aus, dass die Antworten Dir weitergeholfen haben.
    Solltest Du noch "Rückfragen" dazu haben, so gib uns bitte Bescheid.

    Grüße,
    Robert


    Robert Breitenhofer, MICROSOFT  Twitter Facebook
    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Freitag, 17. Februar 2012 08:38
    Moderator