Laden...

Unregelmässige ThreadAbortException; was ist die Ursache?

Erstellt von Peter Bucher vor 9 Jahren Letzter Beitrag vor 9 Jahren 4.982 Views
Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 9 Jahren
Unregelmässige ThreadAbortException; was ist die Ursache?

Hallo zusammen

Bei einem WCF Service der im IIS gehostet ist, treten unregelmässig ThreadAbortException`s auf.
Der Stacktrace zeigt mir auch, wo dieser auftritt. Zweimal war er an einem Ort, wo ich eine Komponente (https://magick.codeplex.com/) benutze, um Bilder zu bearbeiten.

Also dachte ich zuerst, es liege wohl daran und genau an diesem Ort. Nur war komisch, das monatelang alles lief und in Verbindung mit der Komponente nie ein Fehler auftrat.

Erst kürzlich trat der Fehler auch an einer anderen Stelle auf, die in überhaupt keiner Beziehung zu der Bildbearbeitungskomponente steht.

Mir ist nicht ganz klar, unter welchen Umständen die ThreadAbortException geworfen wird.
Fangen kann man sie zwar, aber dann wird sie anschliessend wieder geworfen. Was mit anderen Exceptions, die auch schon aufgetreten sind, nicht der Fall war.

Kurze Erklärung: Eine WFC-Methode wird aufgerufen und innerhalb von dieser kommt folgender Aufruf, um die Arbeit in einem eigenen Thread laufen zu lassen. Anschliessend kommt die WCF-Methode an ihr Ende und hat dann auch keine Verbindung zum Client mehr.


Task.Factory
    .StartNew(() => this.Sync(0, syncTaskStatus), TaskCreationOptions.LongRunning)
    .ContinueWith(task => blSyncTaskInstance.SetFinished(syncTaskId));

Die Methode "this.Sync(0, status)" läuft ca. 2 Stunden.

Ist es jetzt so, dass immer eine ThreadAbortException im Anschluss geworfen wird, wenn irgend eine Exception innerhalb des besagten Threads auftritt?
Oder kann es auch sein dass diese Exception auftritt, weil z.B. der IIS (Host) den ApplicationPool abräumt / oder Timeout, also durch einen externen Einfluss, der den Thread beendet?

Rekonstruktion im lokalen Entwicklersystem ist schwierig. Dort lief der Hintergrundtask sogar mehrere Stunden, ohne das ein Fehler auftrat.

Folgend eine Auswahl an Exceptions die auftreten:

Fehlermeldung:
'System.Threading.ThreadAbortException: Thread was being aborted.
at Magick.Image.write(Image* , Blob* )
at ImageMagick.MagickWriter.Write(Image* image, Blob* blob)
at ImageMagick.MagickWriter.Write(Image* image, Stream stream)
at MyCompany.Libs.MyLib.Sync.Actions.ResizeAction.ExecuteAction(Stream sourceStream)

Fehlermeldung:
'System.Threading.ThreadAbortException: Thread was being aborted.
at Magick.Image.resize(Image* , Geometry* )
at ImageMagick.MagickImage.Resize(MagickGeometry geometry)
at MyCompany.Libs.MyLib.Sync.Actions.ResizeAction.ExecuteAction(Stream sourceStream)
at MyCompany.Libs.MyLib.Sync.SyncFolder.GetStreamChainResult(Stream sourceStream, List`1 preCopyActions)

Durch Webdav-Komponente...> Fehlermeldung:

'System.Exception: System.Threading.ThreadAbortException: Thread was being aborted.
at System.Net.UnsafeNclNativeMethods.OSSOCK.recv(IntPtr socketHandle, Byte* pinnedBuffer, Int32 len, SocketFlags socketFlags)
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, SocketError& errorCode)
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.PooledStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.Connection.SyncRead(HttpWebRequest request, Boolean userRetrievedStream, Boolean probeRead)
at System.Net.ConnectStream.ProcessWriteCallDone(ConnectionReturnResult returnResult)
at System.Net.ConnectStream.CallDone(ConnectionReturnResult returnResult)
at System.Net.ConnectStream.CloseInternal(Boolean internalCall, Boolean aborting)
at System.Net.ConnectStream.System.Net.ICloseEx.CloseEx(CloseExState closeState)
at System.Net.HttpWebRequest.EndWriteHeaders_Part2()
at System.Net.HttpWebRequest.EndWriteHeaders(Boolean async)
at System.Net.HttpWebRequest.WriteHeadersCallback(WebExceptionStatus errorStatus, ConnectStream stream, Boolean async)
at System.Net.ConnectStream.WriteHeaders(Boolean async)
at System.Net.HttpWebRequest.EndSubmitRequest()
at System.Net.Connection.SubmitRequest(HttpWebRequest request, Boolean forcedsubmit)
at System.Net.ServicePoint.SubmitRequest(HttpWebRequest request, String connName)
at System.Net.HttpWebRequest.SubmitRequest(ServicePoint servicePoint)
at System.Net.HttpWebRequest.GetResponse()
at Independentsoft.Webdav.Resource.GetInputStream(String address)

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

M
171 Beiträge seit 2012
vor 9 Jahren

Mir ist nicht ganz klar, unter welchen Umständen die ThreadAbortException geworfen wird.
Fangen kann man sie zwar, aber dann wird sie anschliessend wieder geworfen. Was mit anderen Exceptions, die auch schon aufgetreten sind, nicht der Fall war.

ThreadAbortException wird geworfen, wenn Thread.Abort() aufgerufen wird. Der Thread wird durch die Methode im Prinzip hart, durch Werfen dieser Exception beendet. Besser ist es, einen Thread sauber und kontrolliert zu beenden. Vielleicht arbeitet Deine Komponente intern mit Thread.Abort, dann wirst Du wenig tun können.

16.833 Beiträge seit 2008
vor 9 Jahren

Der IIS räumt nur automatisch weg, wenn auf den Service länger als die Idle Time (Default: 20Min) des AppPools kein Zugriff erfolgt ist.
Mit dem IIS 7.5 wird aber erkannt, ob ein Request (Dein 2 Stunden Ding) noch aktiv ist oder nicht - dann wird auch nicht weggeräumt.

Du kannst bei Tasks aber sowas mitgeben:

task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);

, sodass Exceptions von Tasks besser abgefangen werden.

Für mich sieht das so aus (weil Du Tasks verwendest), dass die AbortException nicht von Deinem Code, sondern von den Komponenten kommt. Vielleicht arbeiten die unverträglicherweise mit Threads.
Oder aber es ist wirklich ein Task-Fehler, der ebenfalls durch die Background-Eigenschaft als Thread läuft.

S
322 Beiträge seit 2007
vor 9 Jahren

Hallo Peter,

hast Du mal in die Ereignisanzeige von Windows geschaut? Dort steht manchmal mehr drin. Vielleicht ist die ThreadAbortException ein Folgefehler...

Gruß
steffen_dec

Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 9 Jahren

Hallo Abt

Der IIS räumt nur automatisch weg, wenn auf den Service länger als die Idle Time (Default: 20Min) des AppPools kein Zugriff erfolgt ist.
Mit dem IIS 7.5 wird aber erkannt, ob ein Request (Dein 2 Stunden Ding) noch aktiv ist oder nicht - dann wird auch nicht weggeräumt.

Aktiv ist der AppPool nur, wen innerhalb von 20min wieder Requests reinkommen, oder auch, wenn eine langlaufende Methode in einem anderen Thread läuft?

Der Service läuft auf einem IIS 7.5.

Du kannst bei Tasks aber sowas mitgeben:

task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);  
  

, sodass Exceptions von Tasks besser abgefangen werden.

Danke für den Tipp.

Für mich sieht das so aus (weil Du Tasks verwendest), dass die AbortException nicht von Deinem Code, sondern von den Komponenten kommt. Vielleicht arbeiten die unverträglicherweise mit Threads.
Oder aber es ist wirklich ein Task-Fehler, der ebenfalls durch die Background-Eigenschaft als Thread läuft.

In meinem Code benutze ich an keiner Stelle Thread.Abort(), also wird es wohl von der Komponente kommen.

Dein zweiter Satz verstehe ich nicht. Meinst du damit dass irgendeine Exception die Auftritt, dann durch die Task-API als ThreadAbortException weitergeworfen wird?

Schaue mir auch mal die Ereignisanzeige an.

Danke für eure Hilfe bisher.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 9 Jahren

Hallo zusammen

Könnte es einen Unterschied bezüglich meinen Problemen machen, ob ich die übliche Thread-Klasse nutze, oder die neueren Task-Klassen?

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

16.833 Beiträge seit 2008
vor 9 Jahren

Aktiv ist der AppPool nur, wen innerhalb von 20min wieder Requests reinkommen, oder auch, wenn eine langlaufende Methode in einem anderen Thread läuft?

Der Service läuft auf einem IIS 7.5.

Also wenn der Request 2 Stunden dauert dann klappt das. Auslagern und 2 Stunden rennen lassen glaube ich nicht. Bin mir aber nicht sicher.
Es gibt ab IIS 8 eine Extention "Application Initialization", wodurch Du im Menü eine "Start Mode = AlwaysRunning" Option hast. Für 7.5 ist sie über den Wep Plattform Installer nachinstallierbar.

Meinst du damit dass irgendeine Exception die Auftritt, dann durch die Task-API als ThreadAbortException weitergeworfen wird?

Ein Task verwendet im Hintergrund auch einen Thread.
Task ist eine Zwischenschicht, die das Thread-Handling durch einen Scheduler optimiert (u.a. unnötiges Allokieren verhindert).
Daher kann auch ein Task eine ThreadAbortException werfen; was aber unüblich ist.

Sowas kommt eher davon, dass in einem Task ein weiterer Thread gestartet wird, der Abort() aufruft. Dadurch schmiert ein Task durch eine unbehandelte Exception ab.
Das kannst Du aber mit ContinueWith abfangen.

Könnte es einen Unterschied bezüglich meinen Problemen machen, ob ich die übliche Thread-Klasse nutze, oder die neueren Task-Klassen?

Nein, siehe oben.

Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 9 Jahren

Hoi Abt

Also wenn der Request 2 Stunden dauert dann klappt das. Auslagern und 2 Stunden rennen lassen glaube ich nicht. Bin mir aber nicht sicher.
Es gibt ab IIS 8 eine Extention "Application Initialization", wodurch Du im Menü eine "Start Mode = AlwaysRunning" Option hast. Für 7.5 ist sie über den Wep Plattform Installer nachinstallierbar.

Also ich habe die Erfahrung gemacht, dass wenn der Request lange läuft, dann Probleme aufgetaucht sind.
Daher habe ich ja die langlaufende Methode überhaupt erst in einen Thread ausgelagert und den Request nur eine ID zurückgeben lassen, womit ich auf den Status zugreifen kann. So traten die Probleme nicht mehr auf, die ich vorher hatte.

Das AlwaysRunning ist meines Wissens nur dazu da, falls der AppPool recycled wurde oder noch kein einziger Request stattgefunden hat, die Anwendung warmzustarten, sodass es nicht beim ersten Request oder beim ersten Request innerhalb des Idle-Timeouts eine Verzögerung auftritt.

Wenn das so wäre, würde das ja bei meinem Problem nicht wirklich helfen, sondern wäre nur eine Verbesserung.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

M
171 Beiträge seit 2012
vor 9 Jahren

Kann man nicht mit einem der globalen Exceptionhandler die AbortExceptions wegfangen ? Dann würde es zumindest nicht mehr crashen. ThreadAbortException deutet ja normal auch nicht auf einen Fehler hin, sondern ist eine etwas fragwürdige Methode, einen Thread zu beenden. Die fliegt auch meines Wissens nach nicht von selbst, sondern nur durch explizites Aufrufen von Thread.Abort() - Insofern wäre das Wegfangen und Ignorieren doch zumindest nicht falsch?

16.833 Beiträge seit 2008
vor 9 Jahren

Ja stimmt, AlwaysRunning startet den Service auch ohne Request.
Bleibt wohl das Idle auf 0.

Wenn der Request zu lange dauert brechen die meisten Browser die Anforderung ab.
Da lohnt sich dann das Auslagern in extra LR-Threads. ABER: die Gefahr, dass diese abschmieren, ist riesig. Recycle, Restarts könne ja oft auftreten (zB Config geändert).

Ich verwende selbst LR-Threads so wenig wie möglich und lager das ganze auf Windows Services aus. Die kommunizieren dann mit der Webanwendung und übernehmen die LR-Threads.

Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 9 Jahren

Hallo Abt

Wenn der Request zu lange dauert brechen die meisten Browser die Anforderung ab.
Da lohnt sich dann das Auslagern in extra LR-Threads. ABER: die Gefahr, dass diese abschmieren, ist riesig. Recycle, Restarts könne ja oft auftreten (zB Config geändert).
Ich verwende selbst LR-Threads so wenig wie möglich und lager das ganze auf Windows Services aus. Die kommunizieren dann mit der Webanwendung und übernehmen die LR-Threads.

Ich konsumiere die Services wieder mit .NET. Nicht direkt über Browser, sondern über einen Client. Aber auch dort gibt es das "störende", fixe Timeout. Darum kappe ich die Verbindung sofort wieder mit Auslagern und einer ID-Rückgabe.
Nächster Schritt für mich zum testen ist auch die Auslagerung in SelfHosting App oder Windows Service, um deine genannten Punkte ausschliessen zu können.

Danke für den Tipp bezüglich Extension für IIS 7.5, wusste ich nicht, könnte sich noch als nützlich erweisen.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 9 Jahren

Hallo zusammen

Das ThreadAbort-Problem konnte ich beheben indem das Timeout hochgestellt wurde. Das reichte allerdings nicht: McAfee Enterprise hat ab und an die web.config angefasst, sodass die Applikation ab und zu wieder beendet wurde.

Bis man mal darauf kommt!

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011