Laden...

[erledigt] Thread wird nicht durch While Schleife abgebrochen

Erstellt von Fietje vor 6 Jahren Letzter Beitrag vor 6 Jahren 4.004 Views
F
Fietje Themenstarter:in
5 Beiträge seit 2017
vor 6 Jahren
[erledigt] Thread wird nicht durch While Schleife abgebrochen

Moin allerseits, bei meinem unten stehendem Code App führe ich in einer WPF zwei Threads neben dem Main Thread aus. Das funktioniert auch soweit (Stream funktioniert, alle Controls ansprechbar), allerdings schaffe ich es nicht, beide Threads zu beenden. Ausgehend von dieser Seite https://msdn.microsoft.com/de-de/library/7a2f3ay4(v=vs.90).aspx sollte eine While Schleife dafür sorgen, das sich die Threads beenden. Beim Debugging merke ich aber, das die While Schleife trotz geänderter Bedingung nicht reagiert. Nun meine Frage:

  1. Warum bricht trotz !beende = false die While Schleife nicht ab bzw. warum kommt das Prog nicht mal bis zu dem Punkt beim Debugging?

  2. Sollte ich statt Threads lieber mit Dispatcher.Invoke oder Tasks arbeiten?

Grüße Fietje

(Benutze VS2017 mit NAudio 1.8.0)



        public volatile bool beende;
        private string test_url_info_radio = "http://inforadio.de/livemp3";
        private Stream ms = new MemoryStream();
        private bool stream = true;

        void Run()
        {
            while (!beende)
            {
                Thread thread = new Thread(delegate ()
                {
                    while (!beende)
                    {
                        var response = WebRequest.Create(test_url_info_radio).GetResponse();
                        using (var stream = response.GetResponseStream())
                        {
                            byte[] buffer = new byte[65536];
                            int read;
                            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                var pos = ms.Position;
                                ms.Position = ms.Length;
                                ms.Write(buffer, 0, read);
                                ms.Position = pos;
                            }
                        }
                    }
                });

                if (stream == true)
                {
                    thread.Start();
                }
                else
                {
                    return;
                }

                while (ms.Length < 65536 * 5)

                    Thread.Sleep(1000);

                ms.Position = 0;

                using (WaveStream blockAlignedStream = new  BlockAlignReductionStream(WaveFormatConversionStream.CreatePcmStream(new Mp3FileReader(ms))))
                {
                    using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                    {
                        waveOut.Init(blockAlignedStream);
                        waveOut.Play();
                        while (waveOut.PlaybackState == PlaybackState.Playing)
                        {
                            Thread.Sleep(100);
                        }
                    }
                }
            }
        }

        private void play_Click(object sender, RoutedEventArgs e)
        {
            beende = false;
            Thread t = new Thread(delegate () { Run(); });
            t.Start();
        }

        private void stop_Click(object sender, RoutedEventArgs e)
        {
            beende = true;
        }

16.842 Beiträge seit 2008
vor 6 Jahren

In diesem Fall gibt es keine Notwendigkeit mit Threads zu arbeiten; verwende stattdessen Tasks.

Tasks sind eine Abstraktionsebene von Threads, die sich im Gegensatz zu Threads auch abbrechen lassen (Cancellation).
Zudem lassen sie sich viel einfacher programmieren und verwalten, sind Ressourcen-schonender und man kann direkt via async/await arbeiten.

Threads muss man seit Tasks nur noch selten direkt nutzen.
Macht das .NET Leben einfacher.

PS: Trenne Veranntwortlichkeiten im Code durch entsprechende Methoden.
Bastle nicht eine Monster-Run-Methode mit 500 Zeilen.

Warum Dein Code nicht so läuft wie Du denkst: dafür hilft der Debugger am Besten.
[Artikel] Debugger: Wie verwende ich den von Visual Studio?
Prinzipiell sehen die beiden Whiles übereinander logisch nicht korrekt aus.

C
2.122 Beiträge seit 2010
vor 6 Jahren

Ich finds zum lernen schon gut sich mit Threads zu beschäftigen. Die ganzen frischen Neulinge können sonst nur noch das ganze abstrahierte Zeug und haben keine Ahnung mehr was sie da tun. Dann kommen Aussagen raus wie "Tasks gehen ohne Threads, das ist viel besser", wo man nicht mehr weiß ob man überhaupt noch anfangen will zu erklären.

Wieso machst du Threads ineinander? Ein while das lauter Threads hintereinander erzeugt und startet? Ich glaube das ist noch etwas undurchdacht.

16.842 Beiträge seit 2008
vor 6 Jahren

D.h. wenn jemand C# lernt, sollte er eigentlich auch C++ lernen, damit er weiß, was Pointer sind, was C# einem abnimmt?
Ne, die Logik geh ich nicht mit.

Es ist für die allgemeine Aufgabenbewältigung durchaus in Ordnung, wenn man nur das Abstrakte verwendet.
Macht auch das Lernen einfacher, weil man nicht zu viele Infos auf einmal braucht - mein Gedankengang.

C
2.122 Beiträge seit 2010
vor 6 Jahren

(

C++ lernen sollte er nicht, aber was eine Referenz ist sollte man ja wissen. Und das ist doch letztendlich ein Pointer.

P
1.090 Beiträge seit 2011
vor 6 Jahren

Ich sehe jetzt nicht das beende hier auf true gesetzt wird. Die Schleife wird also schon nicht abgebrochen.


 while (!beende)
                    {
                        var response = WebRequest.Create(test_url_info_radio).GetResponse();
                        using (var stream = response.GetResponseStream())
                        {
                            byte[] buffer = new byte[65536];
                            int read;
                            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                var pos = ms.Position;
                                ms.Position = ms.Length;
                                ms.Write(buffer, 0, read);
                                ms.Position = pos;
                            }
                        }
                    }

Edit:
Sorry mein Fehler erst mal alles durchschauen und dann Posten. beenden wird ja im Button Click gesetzt. Du musst es aber auf True setzen.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

F
Fietje Themenstarter:in
5 Beiträge seit 2017
vor 6 Jahren

Tasks sind eine Abstraktionsebene von Threads, die sich im Gegensatz zu Threads auch abbrechen lassen (Cancellation).
Zudem lassen sie sich viel einfacher programmieren und verwalten, sind Ressourcen-schonender und man kann direkt via async/await arbeiten.

Schau ich mir auf jeden Fall heute noch an.

Wieso machst du Threads ineinander? Ein while das lauter Threads hintereinander erzeugt und startet? Ich glaube das ist noch etwas undurchdacht.

Das kommt daher, das ich die Run() ursprünglich in einer stream test single klasse benutzt habe. Als ich das jetzt in ein WPF als Klasse einbauen wollte, hatte ich Probleme nach der Instanzierung. Da lief dann nur noch der neue Thread (Stream ging) aber es gab kein Zugriff auf die WPF Controls im MainWindow mehr. Also hab ich die Run() ebenfalls in nem neuem Thread laufen lassen und somit wieder Zugriff auf die Controls bekommen, daher musste ich dann dort die While Schleifen setzen. Und damit komme ich wieder zum eigentlichen Punkt, die Threads ließen sich auf diese Art und Weise aber nicht mehr beenden. Weder mit Abort() (ich weiß mittlerweile, in dem Fall sehr schlechte Wahl) noch mit der While Schleife kam ich zu nem sinnvollem Ergebnis. Der gepostete Code ist mein Versuch das Problem ausserhalb des Projekts zu beheben.

Edit: beende = true wird mit Klick auf StopButton gesetzt

        private void stop_Click(object sender, RoutedEventArgs e)
        {
            beende = true;
        }
P
1.090 Beiträge seit 2011
vor 6 Jahren

Hast du mal geschaut ob hier die Abbruch Bedingung erfüllt wird.

  while (ms.Length < 65536 * 5)

       Thread.Sleep(1000); 

Ich bin mir bei while da ehrlich gesagt nicht sicher, bei if wird wenn ich keine Klammern verwende nur die nächste Zeile Ausgeführt.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

3.170 Beiträge seit 2006
vor 6 Jahren

Hallo,

die Schleife in Run(), die jedesmal einen Thread startet, sicht grauselig aus - aber das wurde ja schon angemerkt.

Der Kern des Problems aus der Fragestellung ist der:

                            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                var pos = ms.Position;
                                ms.Position = ms.Length;
                                ms.Write(buffer, 0, read);
                                ms.Position = pos;
                            }

Dieser inneren Schleife (im inneren Thread) ist es völlig schnuppe, ob beende auf true gesetzt wird. Die läuft also munter weiter, solange Daten ankommen. Und damit auch der (innere) Thread.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

F
Fietje Themenstarter:in
5 Beiträge seit 2017
vor 6 Jahren

Hast du mal geschaut ob hier die Abbruch Bedingung erfüllt wird.

  while (ms.Length < 65536 * 5)  
  
       Thread.Sleep(1000);  

Ich bin mir bei while da ehrlich gesagt nicht sicher, bei if wird wenn ich keine Klammern verwende nur die nächste Zeile Ausgeführt.

Die Abbruch Bedingung wurde erfüllt, habe jetzt trotzdem mal Klammern gesetzt.

Der Kern des Problems aus der Fragestellung ist der:

                            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)  
                            {  
                                var pos = ms.Position;  
                                ms.Position = ms.Length;  
                                ms.Write(buffer, 0, read);  
                                ms.Position = pos;  
                            }  

Dieser inneren Schleife (im inneren Thread) ist es völlig schnuppe, ob beende auf true gesetzt wird. Die läuft also munter weiter, solange Daten ankommen. Und damit auch der (innere) Thread.

Ich hatte dort auch schon mal die Abbruch Bedingung gesetzt, allerdings mit mäßigem Erfolg, weil die der Rest nicht stimmig war. Aber es hat mir trotzdem gerade eben weitergeholfen. Der nachfolgende Code funktioniert jetzt mit Play und Stop Button trotz grausigen Programmierstils. Für den Anfang reicht erstmal die reine Funktionalität. Ich habe die Abbruch Bedingung !beende überall dort gesetzt, wo es mir sinnvoll erschien. Danke an alle! Schönen Abend noch...


        public volatile bool beende;
        private string test_url_info_radio = "http://inforadio.de/livemp3";
        private bool stream = true;

        void Run()
        {
            while (!beende)
            {
                Stream ms = new MemoryStream();
                Thread thread = new Thread(delegate ()
                        {
                            while (!beende)
                            {
                                var response = WebRequest.Create(test_url_info_radio).GetResponse();
                                using (var stream = response.GetResponseStream())
                                {
                                    byte[] buffer = new byte[65536];
                                    int read;
                                    while ((read = stream.Read(buffer, 0, buffer.Length)) > 0 && !beende)
                                    {
                                        var pos = ms.Position;
                                        ms.Position = ms.Length;
                                        ms.Write(buffer, 0, read);
                                        ms.Position = pos;
                                    }
                                }
                            }
                        });

                if (stream == true)
                {
                    thread.Start();
                }
                else
                {
                    return;
                }

                while (ms.Length < 65536)
                {
                    Thread.Sleep(1000);
                }
                ms.Position = 0;


                using (WaveStream blockAlignedStream = new BlockAlignReductionStream(WaveFormatConversionStream.CreatePcmStream(new Mp3FileReader(ms))))
                {
                    using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                    {
                        waveOut.Init(blockAlignedStream);
                        waveOut.Play();
                        while (waveOut.PlaybackState == PlaybackState.Playing && !beende)
                        {
                            Thread.Sleep(100);
                        }
                    }
                }
            }
        }

        private void play_Click(object sender, RoutedEventArgs e)
        {
            beende = false;
            Thread t = new Thread(delegate () { Run(); });
            t.Start();
        }

        private void stop_Click(object sender, RoutedEventArgs e)
        {
            beende = true;
        }
Hinweis von Abt vor 6 Jahren

Keine Notwendigkeit alles in fett zu schreiben.

4.942 Beiträge seit 2008
vor 6 Jahren

Hallo,

das Nichtbeenden der Schleife wird daran liegen, daß die Stream.Read-Methode blockiert, d.h. es kann daher nicht die Schleifenbedingung abgefragt werden. Du müßtest diesen Stream erst noch schließen (o.ä.) und dann eine evtl. Exception abfangen.

PS: Du verwendest 'stream' für 2 verschiedene Variablen (bool und Stream) - das ist sehr verwirrend zu lesen.

C
2.122 Beiträge seit 2010
vor 6 Jahren
  while (ms.Length < 65536 * 5)

       Thread.Sleep(1000);

Sicher dass die Zahl erreicht wird? Bevor nicht die geforderte Datenmenge da ist läuft die Schleife immer im Kreis.

                            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                var pos = ms.Position;
                                ms.Position = ms.Length;
                                ms.Write(buffer, 0, read);
                                ms.Position = pos;
                            }

Das ist auch seltsam. Es wird an ms.Position geschrieben und danach die Position wieder auf den Wert gesetzt der vorher war. Also werden alle Daten immer auf der selben Position überschrieben. Macht das wirklich was du willst, oder sieht es nur danach aus?

Ich würde mir das nochmal durchsehen und sauberer machen, auch wenn du bisher damit zufrieden bist.

F
Fietje Themenstarter:in
5 Beiträge seit 2017
vor 6 Jahren

das Nichtbeenden der Schleife wird daran liegen, daß die Stream.Read-Methode blockiert, d.h. es kann daher nicht die Schleifenbedingung abgefragt werden. Du müßtest diesen Stream erst noch schließen (o.ä.) und dann eine evtl. Exception abfangen.

Exakt, das war einer der Punkte warum es nicht funktioniert hat.

                        waveOut.Init(blockAlignedStream);
                        waveOut.Play();
                        while (waveOut.PlaybackState == PlaybackState.Playing && !beende)
                        {
                            Thread.Sleep(100);
                        }

Ebenso bei dieser While Schleife, sonst läuft das Playback ausm Buffer noch weiter, obwohl man schon Stopp gedrückt hat.

Sicher dass die Zahl erreicht wird? Bevor nicht die geforderte Datenmenge da ist läuft die Schleife immer im Kreis.

Genau, wirkte sich bis jetzt nur zeitlich aus, da halt länger gebuffert wird. Hatte es erst noch höher. Ansonsten läuft es aber bis jetzt stabil, werde da aber trotzdem noch sehr lange mit den Größen (Chunks etc) hantieren und verschiedene Szenarien ausprobieren.

Das ist auch seltsam. Es wird an ms.Position geschrieben und danach die Position wieder auf den Wert gesetzt der vorher war. Also werden alle Daten immer auf der selben Position überschrieben. Macht das wirklich was du willst, oder sieht es nur danach aus?

Wie geschrieben, bis jetzt läuft der Stream einwandfrei ohne Verzerrungen oder sonstiges. Ich weiß, der Stil ist grauenhaft, aber es funktioniert soweit. Es ist auch nur zum Ausprobieren um mich mit der Materie(Threading, Streaming etc) bekannt zu machen. Solche Sachen wie ein bool zweimal mit dem selbem Namen zu verwenden kommen dann meistens um 02:00 nachts und sind auch nicht an der Tagesordnung. Ich habe die Stream Methode auch nur etwas umgeschrieben und sie als Thread Delegat in mein Projekt eingebaut. Hier noch mal der Link falls da jemand mal Interesse hat:
https://stackoverflow.com/questions/184683/play-audio-from-a-stream-using-c-sharp

Grüße Fietje

F
Fietje Themenstarter:in
5 Beiträge seit 2017
vor 6 Jahren

Habe es jetzt doch noch mal ohne zusätzliche While Schleife überarbeitet. Grüße Fietje.


using NAudio.Wave;
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows;

namespace Organizer
{
    class Streaming_http
    {
        Organizer_MainView Omv;
        Stream MemoryStream;

        private volatile bool close_stream;

        public Streaming_http(Organizer_MainView omv)
        {
            Omv = omv;
        }

        void Run()
        {
            Thread buffering = new Thread(Buffering);
            Thread playback = new Thread(Playback);
            buffering.Start();
            playback.Start();
        }

        void Buffering()
        {
            MemoryStream = new MemoryStream();

            try
            {
                var response = WebRequest.Create(Omv.Url).GetResponse();
                using (var stream = response.GetResponseStream())
                {
                    byte[] buffer = new byte[65536];
                    int read;
                    while ((read = stream.Read(buffer, 0, buffer.Length)) > 0 && !close_stream)
                    {
                        var pos = MemoryStream.Position;
                        MemoryStream.Position = MemoryStream.Length;
                        MemoryStream.Write(buffer, 0, read);
                        MemoryStream.Position = pos;

                        Omv.Received_bytes = MemoryStream.Length.ToString();
                        Omv.Streamposition = MemoryStream.Position.ToString();
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }


        void Playback()
        {
            while (MemoryStream.Length < 65536)
            {
                Thread.Sleep(1000);
            }

            MemoryStream.Position = 0;


            using (WaveStream blockAlignedStream = new BlockAlignReductionStream(WaveFormatConversionStream.CreatePcmStream(new Mp3FileReader(MemoryStream))))
            {
                using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                {
                    Omv.State = "Playing:";
                    waveOut.Init(blockAlignedStream);
                    waveOut.Play();
                    while (waveOut.PlaybackState == PlaybackState.Playing && !close_stream)
                    {
                        Thread.Sleep(100);
                    }
                }
            }
        }

        private void play_Click(object sender, RoutedEventArgs e)
        {
            Run();
            close_stream = false;
        }

        private void stop_Click(object sender, RoutedEventArgs e)
        {
            close_stream = true;
        }
    }
}