Timeout für langwierigen Vorgang

  • VB.NET

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von Samus Aran.

    Timeout für langwierigen Vorgang

    Ich hab hier ein Programm, das die Durchschnittsfarbe des aktuellen Wallpapers berechnet.
    Das Problem ist: Wenn sich das Programm darin aufhängt und nichts mehr funktioniert, lässt sich das Wallpaper nicht mehr ändern, da mein Programm noch Zugriff darauf nimmt.
    Der Vorgang, mit dem die Farbe berechnet wird, läuft NICHT in nem separaten Thread, deshalb meine Frage: Wie lass ich nen Thread laufen, in dem mehrmals Properties von threadfremden Objekten überprüft werden, und der am Ende nen Wert zurückgeben soll? Wenn der Thread länger als ca. 5 Sekunden beschäftigt ist, soll er abgebrochen werden und ein generischer Wert zurückgegeben werden.
    Ich bin mit Threading und solchem Kram heillos überfordert, deshalb frag ich :D
    ich würde auch den backgroundworker verwenden da er:
    1. einen fortschritt meldet
    2. abbrechbar ist (der timer kann also nach 5 sekunden sagen "ätsch! zu spät")
    3. du kannst in dem backgroundworker auf variablen zugreifen und diese ändern, ist einfacher als sich selbst einen thread zu basteln und mit invokes rumzuschlagen

    und nochwas: sieh dir Lockbits an bzw benutze die Fastbitmap Lib aus dem forum hier. bringt die ein geschwindigkeitszuwachs (sofern du getpixel der bitmapklasse verwendest) von faktor 10!
    Ich nutz weder GetPixel noch LockBits, das Problem ist nur, dass meine Methode ab und zu random Exceptions wirft, die bei nem erneuten Aufruf nicht geworfen werden.
    Immer, wenn ne Exception kommt, ruf ich die Methode erneut auf, das führt manchmal zu längeren Schleifen ^^
    cool, wie funktioniert das sonst wenn ich fragen darf? iwo müssen ja die random exceptions herkommen xD
    sollte dann aber trotzdem kein problem sein das ganze in mit backgroundworker unterstüztung zu realisieren. ein BGW ist ja streng genommen nichts anderes als ein Thread mit ein paar erweiterungen die dem user das nutzen vereinfachen..
    Kopier Dir das BackgroundBild in eine eigene Bitmap (nicht über New Bitmap(FILE), sondern über einen Stream), mit der kannst Du machen, was immer Du willst, ohne dass das Wallpaper behelligt wird.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Was passiert denn mit dieser, wenn Du eine eigene Bitmap verwendest?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Kleines Workaround mit Timer:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. WithEvents t As New Timers.Timer
    3. Public AbbruchFlag As Boolean = False
    4. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    5. t.Interval = 1000 * 5
    6. t.Start()
    7. For q = 1 To 100000
    8. For w = 1 To 1000000
    9. For z = 1 To 100000
    10. REM
    11. If AbbruchFlag Then Exit For
    12. Next
    13. If AbbruchFlag Then Exit For
    14. Next
    15. If AbbruchFlag Then Exit For
    16. Next
    17. MessageBox.Show("Vorgang ist fertig...")
    18. End Sub
    19. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    20. Button1.Text = "Starte langen Vorgang..."
    21. End Sub
    22. Sub goforit() Handles t.Elapsed
    23. AbbruchFlag = True
    24. End Sub
    25. End Class
    @RodFromGermany: Ich weiß gerade nicht, was du meinst, aber laut IntelliTrace werden entweder OutOfRange oder OutOfMemory-Exceptions geworfen.
    Edit:

    VB.NET-Quellcode

    1. Dim fs As New IO.FileStream(GetCurrentWallpaper, IO.FileMode.Open, IO.FileAccess.Read)
    2. Dim orig As Bitmap = CType(Bitmap.FromStream(fs, True, False), Bitmap)
    3. fs.Close()
    4. fs.Dispose()
    So mach ichs derzeit.

    @der_Kurt: Das werd ich mal probieren, danke.

    Samus Aran schrieb:

    @RodFromGermany: Ich weiß gerade nicht, was du meinst

    Nimm ein Bild, das nicht Wallpaper ist.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    so, ich habe mich mal an einer "async-get-Durchschnittsfarbe"-funktion versucht. Dauert der Vorgang zu lange wird er automatisch (hier nach 5 sekunden) abgebrochen.
    Damit der Programmierer am anderen ende weiß was passiert gibt es einige events: Statusreport in prozent, Timeout-event das aufgerufen wird wenn der vorgang wegen zeitüberschreitung abgebrochen werden musste, ein ergebnis-event das die durchschnittsfarbe zurückgibt und ein event für benutzungsfehler (angegebene datei existiert nicht, der backgroundworker ist bereits beschäftigt).

    hier mein code (einfach eine formsanwendung erstellen, ein label und einen button platzieren und den code einfügen. der rest sollte selbsterklärend sein.

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class Form1
    3. Public WithEvents DFB As New Durchschnittsfarbenberechner
    4. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    5. With New OpenFileDialog
    6. If .ShowDialog = Windows.Forms.DialogResult.OK Then
    7. DFB.Berechne(.FileName)
    8. Button1.Enabled = False
    9. End If
    10. End With
    11. End Sub
    12. Private Sub DFB_Ergebnis(ByVal col As System.Drawing.Color) Handles DFB.Ergebnis
    13. MsgBox("Farbe erfolgreich berechnet!")
    14. Button1.BackColor = col
    15. Button1.Enabled = True
    16. End Sub
    17. Private Sub DFB_Fehler(ByVal msg As String) Handles DFB.Fehler
    18. MsgBox("ERROR: " & msg)
    19. Button1.Enabled = True
    20. End Sub
    21. Private Sub DFB_Statusreport(ByVal Status As Integer) Handles DFB.Statusreport
    22. Label1.Text = Status.ToString & " %"
    23. End Sub
    24. Private Sub DFB_Timeout() Handles DFB.Timeout
    25. MsgBox("Der Vorgang dauerte zu lange!")
    26. Button1.Enabled = True
    27. End Sub
    28. End Class
    29. Public Class Durchschnittsfarbenberechner
    30. Public TimeoutTime As Integer = 5 'Timeout in Sekunden
    31. Dim TimeoutCounter As Integer = 0 'zählervariable
    32. WithEvents BGW As New System.ComponentModel.BackgroundWorker
    33. WithEvents TimeoutTimer As New Timer
    34. Public Event Ergebnis(ByVal col As Color)
    35. Public Event Fehler(ByVal msg As String)
    36. Public Event Timeout()
    37. Public Event Statusreport(ByVal Status As Integer) 'statusreport in 0-100%
    38. Public Sub New()
    39. BGW.WorkerReportsProgress = True
    40. BGW.WorkerSupportsCancellation = True
    41. TimeoutTimer.Interval = 1000
    42. End Sub
    43. Public Sub Berechne(ByVal Path As String)
    44. If BGW.IsBusy = True Then
    45. RaiseEvent Fehler("Der Backgroundworker ist bereits beschäftigt!")
    46. 'alternativ die arbeit einstellen und die neue aufgabe wahrnehmen
    47. Else
    48. If My.Computer.FileSystem.FileExists(Path) = False Then
    49. RaiseEvent Fehler("Die Datei existiert nicht!")
    50. Else
    51. 'Die arbeit beginnen...
    52. 'Timerreset
    53. TimeoutCounter = 0
    54. TimeoutTimer.Start()
    55. 'backgroundworker anschmeißen
    56. CancelBGW = False
    57. BGW.RunWorkerAsync(Path)
    58. End If
    59. End If
    60. End Sub
    61. Dim Durchschnittsfarbe As Color
    62. Dim CancelBGW As Boolean = False
    63. Private Sub BGW_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGW.DoWork
    64. Dim path As String = CStr(e.Argument)
    65. Dim tmp As Bitmap
    66. Using Str As New IO.StreamReader(path)
    67. tmp = New Bitmap(Str.BaseStream)
    68. End Using
    69. Dim dR As Integer = 0
    70. Dim dG As Integer = 0
    71. Dim dB As Integer = 0
    72. Dim Counter As Integer = 0
    73. Dim MaxCount As Integer = tmp.Size.Width * tmp.Size.Height
    74. 'hier alternativ Lockbits verwenden oder die Fastbitmap Lib aus dem Forum!
    75. For x = 0 To tmp.Size.Width - 1
    76. If CancelBGW Then Exit For
    77. For y = 0 To tmp.Size.Height - 1
    78. If CancelBGW Then Exit For
    79. With tmp.GetPixel(x, y)
    80. dR += .R
    81. dG += .G
    82. dB += .B
    83. End With
    84. Next
    85. Counter += tmp.Size.Height
    86. BGW.ReportProgress(CInt((Counter / MaxCount) * 100))
    87. Next
    88. Durchschnittsfarbe = Color.FromArgb(255, CByte(dR / MaxCount), CByte(dG / MaxCount), CByte(dB / MaxCount))
    89. End Sub
    90. Private Sub BGW_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BGW.ProgressChanged
    91. RaiseEvent Statusreport(e.ProgressPercentage)
    92. End Sub
    93. Private Sub BGW_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BGW.RunWorkerCompleted
    94. TimeoutTimer.Stop()
    95. TimeoutCounter = 0
    96. If CancelBGW = False Then RaiseEvent Ergebnis(Durchschnittsfarbe)
    97. End Sub
    98. Private Sub TimeoutTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles TimeoutTimer.Tick
    99. TimeoutCounter += 1
    100. If TimeoutCounter = TimeoutTime Then
    101. If BGW.IsBusy = True Then
    102. 'BGW.CancelAsync()
    103. CancelBGW = True
    104. TimeoutTimer.Stop()
    105. TimeoutCounter = 0
    106. RaiseEvent Timeout()
    107. End If
    108. End If
    109. End Sub
    110. End Class



    Das Problem ist: Wenn sich das Programm darin aufhängt und nichts mehr funktioniert, lässt sich das Wallpaper nicht mehr ändern, da mein Programm noch Zugriff darauf nimmt.

    Wenn du meine klasse mit der Fastbitmap lib oder lockbits verbesserst sollte die farbe in wenigen sekunden berechnet sein.
    Alternativ könntest du das bild in ein temp-verzeichnis deiner anwendung kopieren, dann wäre das win-wallpaper nicht belastet und du hättest alle zeit der welt für die berechnung

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „FreakJNS“ ()