Thread wird zu früh beendet

  • VB.NET

Es gibt 25 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Thread wird zu früh beendet

    yo Leute,

    Habe ein Problem bei einer e-Mail Function.
    Ich starte in einem eigenen Thread eine Function welche überprüft ob ein Prozess lauft. Lauft dieser nicht wird er gestartet und es wird ein E-Mail versendet um den Support darüber zu informieren.
    Das E-Mail senden ist eine Function einem Modul welche ein E-Mail asyncron versendet (andere Teile des Programms greifen auch auf diese Funktion zu, deshalb asycnron).

    Das Problem ist nun (witzigerweise nur in W2k und XP nicht in W7), dass der Thread dann schon beendet ist bevor das Completed Event des Mailsendens greift. (Quasi mittem im Sendevorgang wird abgebrichen).
    Somit bekomm ich eine Exception

    Spoiler anzeigen

    System.Net.Mail.SmtpException: Fehler beim Senden von Mail. ---> System.IO.IOException: Von der Übertragungsverbindung können keine Daten gelesen werden: Eine Anforderung zum Senden oder Empfangen von Daten wurde verhindert, da der Socket nicht verbunden ist und (beim Senden über einen Datagrammsocket mit einem sendto-Aufruf) keine Adresse angegeben wurde. ---> System.Net.Sockets.SocketException: Eine Anforderung zum Senden oder Empfangen von Daten wurde verhindert, da der Socket nicht verbunden ist und (beim Senden über einen Datagrammsocket mit einem sendto-Aufruf) keine Adresse angegeben wurde
    bei System.Net.Sockets.Socket.BeginReceive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, AsyncCallback callback, Object state)
    bei System.Net.Sockets.NetworkStream.BeginRead(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
    --- Ende der internen Ausnahmestapelüberwachung ---
    bei System.Net.Mail.SmtpConnection.ConnectAndHandshakeAsyncResult.End(IAsyncResult result)
    bei System.Net.Mail.SmtpClient.ConnectCallback(IAsyncResult result)
    --- Ende der internen Ausnahmestapelüberwachung ---


    Starte ich die Funktion ohne eigenem Thread funktioniert es.

    Kann ich irgendwie dem Thread sagen er soll gefälligst warten bis er das Mail versendet hat bevor er sich beendet?

    lg
    ScheduleLib 0.0.1.0
    Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten
    Hi,

    Das ist halt so eine Sache mit Threads. Verwende dafür lieber ein BackgroudWorker. In Threads funkioniert zb. Timer1.Start() nicht. In Threads geht meist vieles verloren. Vor allem wenn es viel Code ist. Du könntest ja die Informationen zwischenspeichern und nur im Thread die versende Funktion nutzen. Wenn das auch noch geht. Wenn nicht, probier mal den BWorker aus.

    Gruß Eydle Soft
    Wozu schreibt man ab, obwohl man es doch kann? :)

    ::Forum für Entwickler hier::
    Eydle Soft, was'n Quatsch. Wenn man anständig programmiert, steht einem Thread einem BackgroundWorker in nichts nach.
    Klar geht der Zugriff auf Controls nicht, weil's einfach zu Thread-Unsafe ist und daher zu undefiniertem Verhalten führen kann.
    In einem Thread geht außerdem nichts verloren. :x
    Du kannst eine Thread zB über ManualResetEvents dazu bringen, zu warten.
    (Also nach dem Async Mail Senden mit .WaitOne() warten, im CompletedEvent mit .Set())

    Jedoch ist sowas oft Unfug. Einen Thread der nur wartet ist quasi Verschwendung.
    Ich denke da solltest du nochmal dein Konzept überdenken.

    zB:
    Vll kannst du die erste Funktion nicht in nem Thread starten sondern asynchron(Mithilfe eines Delegates und .BeginInvoke/.EndInvoke)
    Hast du die Prozesse geprüft/gefunden, feuerst du ein Event zurück in den Hauptthread und startest von diesem den Asynchronen Mailversand.
    Das ist meine Signatur und sie wird wunderbar sein!
    @Eydle Soft: Das System lauft ja gut mit den Threads nur halt nicht das versenden.
    @ErfinderDesRades: Ich arbeite nicht mit Sockets sondern mit Mail. Ich vermute der Fehlermeldung nach, dass die Verbindung welche vorher aufgebaut wurde nicht mehr existent ist und somit auch der Socket nicht mehr vorhanden ist.
    @Mono: Klingt intresannt. An der Umsetzung müsst ich halt tüfteln. Werde am Montag mal schauen ob ichs hinbekomm :)

    lg
    ScheduleLib 0.0.1.0
    Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten

    fichz schrieb:

    @ErfinderDesRades: Ich arbeite nicht mit Sockets sondern mit Mail. Ich vermute der Fehlermeldung nach, dass die Verbindung welche vorher aufgebaut wurde nicht mehr existent ist und somit auch der Socket nicht mehr vorhanden ist.

    Naja, inne Fehlermeldung kommen halt Sockets vor.

    Mein Vorschlag bleibt derselbe: Im NebenThread blockierende Methoden verwenden, kein asynchrones Zeug (wo dann nochn Thread geöffnet wird).

    Geht natürlich nur, wenns blockierende Methoden ühaupt gibt - ich hab ja keine Ahnung, wie dein mail-dingsbums tickt.
    Soda ich habe das ganze es nun wie folgt abgeändert. Nun mit 2 Varianten die funktionieren wobei ich eine davon priorisiere und nur wissen möchte ob das auch soweit korrekt ist:

    Variante 1: (will ich nicht unbedingt verwenden)
    Nach dem Überwachen ist ein Sleep von 10 Sekdunden drin. Das Mail wird versendet und die Form hängt nicht. Jedoch irgendwie unschön.

    Variante 2:
    Das Senden des Mails und das Überwachen der Prozesse wurde nun in 2 Klassen ausgelagert. In der Überwachungsklasse wird nun ein Event gefuert wenn es zum Mailsenden kommt.
    Dieses Event wird anhand eines Delegaten per Me.Invoke an eine Sub weitergeleitet welches eine neue Instanz des Mailsendens erstellt.

    Ich poste hier mal Ausschnitte des Codes.
    Da ich mir nicht sicher bin ob Me.Invoke hier die richtige Lösung ist:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. 'Delegate
    2. Delegate Sub MailSenden(ByVal szMailEmpf As String, ByVal szMailSubj As String, ByVal szMailText As String, ByVal ID As Integer)
    3. 'Event welches gefeuert wird zum Mail versenden
    4. Private Sub SendMail(ByVal szMailEmpf As String, ByVal szMailSubj As String, ByVal szMailText As String, ByVal ID As Integer) Handles Watcher.SendMail
    5. Me.Invoke(New MailSenden(AddressOf doSendWatcherMail), szMailEmpf, szMailSubj, szMailText, ID)
    6. End Sub
    7. ' Sub welches die Mail Klasse instanziert und versendet
    8. Private Sub doSendWatcherMail(ByVal szMailEmpf As String, ByVal szMailSubj As String, ByVal szMailText As String, ByVal ID As Integer)
    9. Dim objMail As New MailClass
    10. objMail.doSendMail(szMailEmpf, szMailSubj, szMailText)
    11. End Sub



    Ist die Überlegung und der Code ansich nun korrekt?

    lg
    ScheduleLib 0.0.1.0
    Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten
    Interessant.
    Hier wird Invoking mal anners verwendet, als ichs gewohnt bin.
    Normalerweise nutzt mans ja, um unzulässige threadübergreifende Zugriffe auf Controls zu vermeiden.

    Hier wirds aber genutzt, um eine nichtblockierende Methode auszuführen auf einem Objekt, welches in einem NebenThread verfallen würde, bevor es mit seim Job fertig ist.

    Im Hauptthread verfällt dieses Objekt natürlich nicht, denn der Hauptthread wird ja nicht beendet, wenn die Methode durch ist.


    Also so würde ich mir jdfs. erklären, falls es tatsächlich so ist, dasses ohne Invoking nicht funktioniert, jetzt aber doch.

    Kannsteja durch eine kleine Ännerung testen:

    VB.NET-Quellcode

    1. 'Event welches gefeuert wird zum Mail versenden
    2. Private Sub SendMail(ByVal szMailEmpf As String, ByVal szMailSubj As String, ByVal szMailText As String, ByVal ID As Integer) Handles Watcher.SendMail
    3. 'Me.Invoke(New MailSenden(AddressOf doSendWatcherMail), szMailEmpf, szMailSubj, szMailText, ID)
    4. doSendWatcherMail(szMailEmpf, szMailSubj, szMailText, ID)
    5. End Sub
    na, dann scheint Invoking auch hierfür die angezeigte Lösung zu sein.

    Du kannst deine Code-Aufteilung übrigens sinnvoll vereinfachen, indem du deine Watcher-Klasse die email selbst verschicken lässt.
    Scheinen ja alle Daten für den Versand in der Watcherklasse vorhanden zu sein, und woanners werden die ja für nix gebraucht.

    Auch vonne Watcherklasse kannst du ein Invoking ausführen, indem du nämlich als invokendes Control einfach Application.OpenForms(0) hernimmst - das MainForm der Anwendung.
    Die Watcherklasse wurde in der Form bereits instanziert.

    VB.NET-Quellcode

    1. Private WithEvents Watcher As New Watcher

    Der Thread startet eine Sub welcher die Sub des Watchers zum checken ausführt.
    Die Watcher Klasse hat ein Event

    VB.NET-Quellcode

    1. Public Event SendMail(ByVal szMailEmpf As String, ByVal szMailSubj As String, ByVal szMailText As String, ByVal ID As Integer)

    Es werden einfach alle Prozesse durchlaufen. Wenn der Task nicht lauft wird er weider gestartet. Nun wird einfach abgefragt ob in der DB eine E-Mail Adresse eingetragen ist und wenn ja wird das Event geraist.

    VB.NET-Quellcode

    1. RaiseEvent SendMail(szMailRecv, szMailSubj, szMailText, -1)


    und dann wird in der MainForm

    VB.NET-Quellcode

    1. Delegate Sub doWatcherMailSending(ByVal szMailEmpf As String, ByVal szMailSubj As String, ByVal szMailText As String, ByVal ID As Integer)
    2. Private Sub SendWatcherMail(ByVal szMailEmpf As String, ByVal szMailSubj As String, ByVal szMailText As String, ByVal nTaskId As Integer) Handles Watcher.SendMail
    3. Me.Invoke(New doWatcherMailSending(AddressOf doSendWatcherMail), szMailEmpf, szMailSubj, szMailText, ID)
    4. End Sub

    ausgeführt.

    VB.NET-Quellcode

    1. Private Sub doSendWatcherMail(ByVal szMailEmpf As String, ByVal szMailSubj As String, ByVal szMailText As String, ByVal nTaskId As Integer)
    2. Dim objMail As New AgentLib.Mail
    3. objMail.doSendMail(szMailEmpf, szMailSubj, szMailText)
    4. End Sub


    Die Mailsender Klasse ist nichts neues.
    msdn.microsoft.com/de-de/library/x5x13z6h.aspx#Y1646
    basiert auf diesem Link

    lg
    ScheduleLib 0.0.1.0
    Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten
    Klar gab es diese Variante, aber es greifen andere Funktionen des Programm auch auf diese Mailsendevariante zu.
    Diese werden aber teilweise nicht in einem eigenen Thread gehandelt und würden dann die Form blockieren.

    lg
    ScheduleLib 0.0.1.0
    Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten
    Nach mehrmaligem Lesen des Threads habe ich das Problem immer noch nicht ganz verstanden.

    Ich schätze mal das läuft so:
    - UI-Thread startet Watcher
    - Watcher startet Thread W und kehrt zurück, W überwacht die Tasks
    - Ein Ereignis tritt ein. Eine Mail soll versendet werden.
    - Der Handler wird in Thread W ausgeführt und sendet eine Mail über eine Bibliothek.
    - Da die Bibliothek SendAsync() verwendet, kehrt Thread W sofort zur Prüfung der Prozesse zurück.
    --> Das klingt bis jetzt vernünftig.

    - Wo ist das Problem?
    Gruß
    hal2000
    Das Problem war:
    - Timer der alle 1 Minuten tickt startet einen Thread.
    - Der Sub in diesem Thread (welcher in der Klasse Watcher ist) prüft die Prozesse ob diese noch laufen.
    - Lauft der Prozess nicht mehr und wird eine E-Mail Adresse in der DB gefunden wird das Event gefeuert zum Mailversenden.
    - Würde ich nun in dem Sub mit diesem Eventhandler den Sub starten welcher das Mail versendet ist der Thread seiner Ansicht nach fertig. Jedoch wird dann das Senden unterbrochen.
    Nun habe ich einen Delegaten erstellt welcher mit Me.Invoke die Mailsende Funktion aus dem Hauptthread startet.
    Funktionieren tut es nun jetzt. Nur ist die Variante richtig?

    lg
    ScheduleLib 0.0.1.0
    Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten
    Ich würde sagen: Du hast recht. Nur ist dies für mich auch ein Projekt wo ich mir generell das Thema Threading ein wenig näherbringen wollte.
    Ist vielleicht oversized - ja - jedoch trotzdem eine berechtigte Frage. (Eventuell gibts ja mal Funktionen die länger brauchen könnten)

    lg
    ScheduleLib 0.0.1.0
    Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten