Thread abort()-> NullReferenceException



  • Nachdem ich gestern mein Progrämmchen endlich mit mehreren Threads ausstatten und starten konnte, wollte ich gerne neben meiner normalen Abbruchbedingung eine Möglichkeit schaffen den Thread direkt zu beenden.

    Der Thread wird hier initialisiert und gestartet:

    Thread^ Thread_calc; 
    
    private: System::Void Start_Click(System::Object^  sender, System::EventArgs^  e) 
    {
    	Thread^ Thread_calc = gcnew Thread( gcnew ThreadStart( this,&Form1::calculate));
    	Thread_calc->Start();
    }
    

    Hier was der Thread machen soll:

    void calculate()
    {
    	try
    	{
            ......
            }
    catch (SystemException^)
    	{
    	this->SetTextInformation(String::Format("Thread - caught ThreadAbortException" ),0);
          }
    }
    

    Und hier soll er abbgebrochen werden:

    private: System::Void Stop_Click(System::Object^  sender, System::EventArgs^  e) 
    {
    	if(messungstop->Text->Length==1)
    		{
    			try
    			{
    				Thread_calc->Abort();
    				//Thread_calc->Join(50);
    			}
    			catch (SystemException^)
    			{
    				this->SetTextInformation(String::Format("Thread is aborting" ),1);
    				//this->SetTextInformation(String::Format("Exception message: {0}", e->Message ),0);
    			}
    
    			ButtonsOn();
    		}
    	if(messungstop->Text->Length==0) 
    		{
    			messungstop->AppendText(String::Format("1"));
    		}
    
    }
    

    Allerdings bekomme ich wenn ich den Thread beende Folgenden Meldung:
    System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
    bei System.Threading.Thread.AbortInternal()
    bei System.Threading.Thread.Abort()
    Und der Thread rechnet einfach weiter, als wär nix gewesen 😞
    Was mach ich falsch?



  • Thread^ Thread_calc = gcnew Thread( gcnew ThreadStart( this,&Form1::calculate));
    

    die Sichtbarkeit Deiner Variablen stimmen nicht - Du hast welche doppelt



  • Danke!!!!
    Das war es wirklich 😃
    Manchmal sind die einfachsten Fehler die schwierigsten 🙂
    Läuft jetzt alles so wie gewünscht.



  • d.h. "Thread_calc" war beim Aufruf von "Thread_calc->Abort()" null...?
    interessant dass dann erst bei System.Threading.Thread.AbortInternal() eine NullReferenceException fliegt, und nicht schon bei System.Threading.Thread.Abort(). von C++ bin ich das gewohnt, von C# hätte ich instiktiv jetzt mal erwartet dass es sofort bei System.Threading.Thread.Abort() schnalzt, auch wenn System.Threading.Thread.Abort() nicht virtual ist und keine Member dereferenziert.
    anscheinend doch nicht so...



  • hustbaer schrieb:

    von C# hätte ich instiktiv jetzt mal erwartet dass es sofort bei System.Threading.Thread.Abort() schnalzt, auch wenn System.Threading.Thread.Abort() nicht virtual ist und keine Member dereferenziert.
    anscheinend doch nicht so...

    Weil es ja auch C# ist? Ich schmeiß mich wech. 🙄
    C++/CLI kommt wohl eher hin.



  • Moin,

    ist mir gar nicht aufgefallen ... also wenn das wirklich der StackTrace war

    System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
    bei System.Threading.Thread.AbortInternal()
    bei System.Threading.Thread.Abort()

    dann würde auf einen Fehler in .NET tippen

    Thread_calc->Abort()
    

    ist zwar in einem Try-Catch eingekapselt - aber die NRE wird nie gefangen ... Thread_calc ist ja an der Stelle NULL ... da müsste der StackTrace eigentlich was wie

    System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
    bei blabla.Stop_Click()
    bei ...

    liefern - macht er aber anscheinend nicht

    hand, mogel

    PS: ick habe noch eine Matjes-Brötchen übrig 🤡



  • Man beachte auch: Thread->Abort() sollte man *nie* verwenden!
    Verwende eine sinnvolle Synchronisierung wie "AutoResetEvent"...



  • Fuchs hol Du die Gans schrieb:

    hustbaer schrieb:

    von C# hätte ich instiktiv jetzt mal erwartet dass es sofort bei System.Threading.Thread.Abort() schnalzt, auch wenn System.Threading.Thread.Abort() nicht virtual ist und keine Member dereferenziert.
    anscheinend doch nicht so...

    Weil es ja auch C# ist? Ich schmeiß mich wech. 🙄
    C++/CLI kommt wohl eher hin.

    Hihi, ja, für mich is das "a nudl a tag".
    Soll heissen: ich meinte das ganze .NET gedöns, und da schreib bzw. denk ich meist einfach "C#", weil das die Sprache ist mit der ich am meisten .NET macht. Zugegebenermassen äusserst schlampig.

    Jochen Kalmbach schrieb:

    Man beachte auch: Thread->Abort() sollte man *nie* verwenden!
    Verwende eine sinnvolle Synchronisierung wie "AutoResetEvent"...

    Wieso?
    Thread.Abort() verursacht das Werfen einer ThreadAbortException in C#. Gibt's da auch bekannte Probleme wie mit ::TerminateThread() u.ä. Problemkindern?



  • mogel schrieb:

    Moin,

    ist mir gar nicht aufgefallen ... also wenn das wirklich der StackTrace war

    System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
    bei System.Threading.Thread.AbortInternal()
    bei System.Threading.Thread.Abort()

    dann würde auf einen Fehler in .NET tippen

    Thread_calc->Abort()
    

    ist zwar in einem Try-Catch eingekapselt - aber die NRE wird nie gefangen ... Thread_calc ist ja an der Stelle NULL ... da müsste der StackTrace eigentlich was wie

    System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
    bei blabla.Stop_Click()
    bei ...

    liefern - macht er aber anscheinend nicht

    hand, mogel

    PS: ick habe noch eine Matjes-Brötchen übrig 🤡

    Mein Fehler! Ich hatte nur die ersten drei Zeilen vom Stacktrace kopiert und ich kann dich beruhigen eine Zeile Später kam der Aufruf bzglt. stop_click();
    Und ja, als der Thread noch doppelt intinalsiert war, ist er nicht in die try / catch des thread_calc reingegangen, sondern schon vorher direkt beim aufruf thread_calc->abort(); abgeschmiert. Konnte das auch nur abfangen, indem ich wie oben zu sehen die thread_calc->abort(); innerhalb einer Try Catch Methode aufgerufen wurde. Seitdem die doppelte Initialisierung raus ist, läuft alles so gewünscht und ich kann die thread_calc->abort(); auch ohne try/catch ausführen.
    Vielen Danke nochmal!
    PS: Ich nehm dein Matjes Brötchen 😃



  • hustbaer schrieb:

    Jochen Kalmbach schrieb:

    Man beachte auch: Thread->Abort() sollte man *nie* verwenden!
    Verwende eine sinnvolle Synchronisierung wie "AutoResetEvent"...

    Wieso?
    Thread.Abort() verursacht das Werfen einer ThreadAbortException in C#. Gibt's da auch bekannte Probleme wie mit ::TerminateThread() u.ä. Problemkindern?

    Nehmen wir an, der Thread macht einen InterOp aufruf und kehr längere zeit nicht zurück... was passiert dann? Nix... deshalb würde ich eher auf *sinnvolle* Synchronisationsmechanismen setzen als auf unsinnige und nicht durchdachte Dinge...
    Von außen eine Exception in einem anderen Thread zu werfen ist architektronisch sowas von daneben... so jemand fliegt bei mir sofort aus dem Team... (oder er kommt in ein 4-Augen-Gespreäch mit mir... dann macht er das nicht mehr).

    Für mich wird sowas *nur* gemacht, wenn man sich über etwas sinnvolles keine Gedanken machen will...



  • Jochen Kalmbach schrieb:

    hustbaer schrieb:

    Jochen Kalmbach schrieb:

    Man beachte auch: Thread->Abort() sollte man *nie* verwenden!
    Verwende eine sinnvolle Synchronisierung wie "AutoResetEvent"...

    Wieso?
    Thread.Abort() verursacht das Werfen einer ThreadAbortException in C#. Gibt's da auch bekannte Probleme wie mit ::TerminateThread() u.ä. Problemkindern?

    Nehmen wir an, der Thread macht einen InterOp aufruf und kehr längere zeit nicht zurück... was passiert dann? Nix... deshalb würde ich eher auf *sinnvolle* Synchronisationsmechanismen setzen als auf unsinnige und nicht durchdachte Dinge...
    Von außen eine Exception in einem anderen Thread zu werfen ist architektronisch sowas von daneben... so jemand fliegt bei mir sofort aus dem Team... (oder er kommt in ein 4-Augen-Gespreäch mit mir... dann macht er das nicht mehr).

    Für mich wird sowas *nur* gemacht, wenn man sich über etwas sinnvolles keine Gedanken machen will...

    Wir haben immer wieder das Problem gehabt das unsere COM-Module (die über die Software angesteuert werden) abstürzen und damit die ganze calculate(); Routine und letztendlich das ganze Program mitreisen. Daher wollte ich die calculate(); Funktion gerne in einem extra Thread ausführen um ihn notfalls abzuschießen. Dies geschieht auch erst bei zweimaliger Ausführung der click_stop(); Funktion. Mit dem ersten Aufruf wird die normale Messung nach der letzten Berechnung beendet. Wenn sich aber wieder eine der COM-Module verabschiedet kommt die Software erst gar nicht zu diesem Punkt und landet in einer Endlosschleife...
    Von daher finde ich als Anfänger die Variante jetzt gar nicht "sooo" schlecht. Obwohl sich Gewiss erfahrende Kollegen die Haare vom Kopfe reissen werden, wenn sie den ganzen Quelltext sehen würden 😃
    Und ich bin ja noch jung und lernfähig. Also Was meinst du genau mit Synchronisationsmechanismen?
    Bin für alle Verbesserungsvorschläge offen!



  • Jochen Kalmbach schrieb:

    hustbaer schrieb:

    Jochen Kalmbach schrieb:

    Man beachte auch: Thread->Abort() sollte man *nie* verwenden!
    Verwende eine sinnvolle Synchronisierung wie "AutoResetEvent"...

    Wieso?
    Thread.Abort() verursacht das Werfen einer ThreadAbortException in C#. Gibt's da auch bekannte Probleme wie mit ::TerminateThread() u.ä. Problemkindern?

    Nehmen wir an, der Thread macht einen InterOp aufruf und kehr längere zeit nicht zurück... was passiert dann? Nix...

    Sehe ich nicht als Problem. Man muss sich darüber klar sein dass das so ist. Klar, man muss halt immer wissen was man tut. Muss man auch wenn man selbst was strickt. Der Interop-Aufruf wird ja z.B. auch nicht unterbrochen wenn man selbst irgendwie mit Events oder Cancel-Flags arbeitet.
    Und wieso gerade AutoResetEvent? Wenn dann würde ich hier eher nen ManualResetEvent verwenden...
    Bzw. in C# auch gerne ein volatile bool .
    (Müsste in C++/CLI auch OK sein, da dort wenn ich mich recht erinnere die selben Garantien bezüglich volatile gelten)

    Von außen eine Exception in einem anderen Thread zu werfen ist architektronisch sowas von daneben...

    Ansichtssache. Wenn der Thread damit rechnet, sehe ich hier kein unüberwindbares Problem.

    so jemand fliegt bei mir sofort aus dem Team... (oder er kommt in ein 4-Augen-Gespreäch mit mir... dann macht er das nicht mehr).

    Wenn du meinst...

    Für mich wird sowas *nur* gemacht, wenn man sich über etwas sinnvolles keine Gedanken machen will...

    Es kann durchaus Sinn machen vorgefertigte Lösungen zu verwenden, statt immer das Rad neu zu erfinden.

    Aber merkst du was? Du hast meine Frage überhaupt nicht beantwortet.
    Ich wollte wissen wieso bzw. wo es Probleme machen könnte, und du hast mir keinen einzigen Grund genannt. Nur geschrieben dass das schlecht ist. Weil das halt so ist. Und dass du Leute die sowas machen am liebsten gleich rauswirfst oder zumindest mal zur Seite nimmst.
    Was würdest du denen denn in so einem Gespräch erzählen, mit was würdest du begründen dass das Mist ist was sie da machen? Oder auch einfach nur "weil ich das sage"?

    Schlimm wäre z.B. wenn die ThreadAbortException an beliebigen Stellen ausgelöst werden könnte. Das könnte zur Folge haben, dass Vorgänge die garantiert "no throw" sind, wie z.B. den Inhalt zweier (Referenz-) Variablen zu tauschen o.ä., auf einmal nichtmehr "no throw" sind, und unterbrochen werden könnten.
    Thread.Abort ist allerding über QueueUserAPC implementiert. Damit ist sichergestellt, dass eine ThreadAbortException immer nur dann generiert werden kann, wenn der Thread in einem "alertable state" ist.



  • Wir haben immer wieder das Problem gehabt das unsere COM-Module (die über die Software angesteuert werden) abstürzen und damit die ganze calculate(); Routine und letztendlich das ganze Program mitreisen.

    In dem Fall würde ich eher zwei Dinge empfehlen:

    1. Den eigentlichen Fehler finden und fixen.

    2. Falls das nicht gehen sollte (weil der Code z.B. nicht von euch ist und ihr den Source nicht habt), dann lager' das ganze lieber gleich in einen eigenen Prozess aus.

    Grund ist jetzt weniger dass ich Thread.Abort für grundsätzlich schlecht und böse halte, sondern eher dass ich den "COM-Modulen" nicht ganz vertrauen würde wenn die so komische Dinge machen wie einfach mal eben abzukacken oder hängenzubleiben.
    Wenn du die in ihre eigenen Prozesse auslagerst, kannst du einfach die Prozesse abschiessen. Und das Hauptprogramm ist wesentlich besser geschützt vor irgendwas was so ein wildgewordenes COM-Modul tun könnte.



  • hustbaer schrieb:

    Wir haben immer wieder das Problem gehabt das unsere COM-Module (die über die Software angesteuert werden) abstürzen und damit die ganze calculate(); Routine und letztendlich das ganze Program mitreisen.

    In dem Fall würde ich eher zwei Dinge empfehlen:

    1. Den eigentlichen Fehler finden und fixen.

    2. Falls das nicht gehen sollte (weil der Code z.B. nicht von euch ist und ihr den Source nicht habt), dann lager' das ganze lieber gleich in einen eigenen Prozess aus.

    Grund ist jetzt weniger dass ich Thread.Abort für grundsätzlich schlecht und böse halte, sondern eher dass ich den "COM-Modulen" nicht ganz vertrauen würde wenn die so komische Dinge machen wie einfach mal eben abzukacken oder hängenzubleiben.
    Wenn du die in ihre eigenen Prozesse auslagerst, kannst du einfach die Prozesse abschiessen. Und das Hauptprogramm ist wesentlich besser geschützt vor irgendwas was so ein wildgewordenes COM-Modul tun könnte.

    Leider können wir an den COM Modulen selber nix mehr ändern. Das sind Prototypen die vor ein paar Jahren entwickelt und produziert wurden. Und zudem nicht die Möglichkeit bieten den Flash neu zuprogrammieren...
    Ich arbeite mit diesen Modulen seit einigen Monate im Rahmen meine HIWI Tätigkeit an der Uni. Leider wird das Projekt jetzt erstmal 4 Monate Ruhen, da ich ein Auslandspraktikum einlege. Wenn ich wieder hier bin und dann auch noch die Profs an dem Thema weiter interessiert sind, werd ich die ganze Kommunikation zwischen den PC und den Modulen selber in einen extra Prozess auslagern. Ich hoffe ich bin dann soweit in der Thematik fortgeschritten das ich dann sowas hinbekomme. Und wenn nicht gibt es ja noch so tolle Foren wie das hier 🙂
    Danke noch mal an alle Helferlein!



  • hustbaer schrieb:

    Von außen eine Exception in einem anderen Thread zu werfen ist architektronisch sowas von daneben...

    Ansichtssache. Wenn der Thread damit rechnet, sehe ich hier kein unüberwindbares Problem.

    Dann zweig mir mal ein *Sinnvolles* Beispiel wo der Thread wirklich damit rechnet. Das macht ja fast nur Sinn, wen der Thread z.B. reine Berechnungsfunktionen macht... aber nicht komplexes IO... und wenn ich dann noch COM höre, dann wird es mir ja gleich ganz schlecht...

    Ich gebe Dir recht: Es gibt einen ganz begrenzten Einsatzbereich für dies... aber ich würde diesen ganz eng eingrenzen und zuerst mir erklären lassen, WIE er damit umgeht bzw. WAS er genau macht.

    Es macht jetzt wenig Sinn die vielen Fälle zu erklären, die Probleme verursachen können. Ich muss natürlich aber auch sagen, dass man es vermutlich in *allen* Fällen richtig implementieren kann. Aber zeig mir mal ein Programmierer der _konsequent_ mit dem ThreadAbort rechnet und dies einbezieht (in womöglich allen Untermethoden)!

    Mein Fazit ist immer noch: Es ist böse. Und nur wenn der Programmierer sich 10000%ig auskennt,macht es sinn. Sonst nicht.



  • @teddds:
    Ich denke ich hab' da was falsch verstanden. Ich dachte an Software-Module, du meinst vermutlich COM im Sinne von RS232 und Module im Sinn von Platinen/externe Geräte.

    Wobei... in dem Fall wäre es vermutlich ideal, wenn die Threads, die über die serielle Schnittstelle mit den Modulen kommunizieren, selbst merken, wenn das Modul nichtmehr korrekt antwortet.
    Dann kann sich entweder der Thread selbst beenden, oder irgend eine "Recovery-Prozedur" anstossen, in dem das COM Modul resettet wird oder etwas in der Art.

    @Jochen Kalmbach:
    Ich fände es auch besser, wenn es vielleicht eher so implementiert wäre, dass das Werfen einer ThreadAbortException nur von (wenigen) bestimmten Funktionen getriggert würde. Ähnlich wie es im C++ Threading Proposal vorgesehen ist.
    Damit kann man dann denke ich auch in realen Programmen gut umgehen.

    Vermutlich hast du Recht, und die Stellen wo in .NET eine ThreadAbortException geworfen werden kann, sind einfach zu viele. Und die Libraries einfach zu schlecht implementiert, dass man sich 100% darauf verlassen könnte, dass kein Mist passieren kann.


Anmelden zum Antworten