none
Schleife pausieren, wegen CPU ausgelastet RRS feed

  • Frage

  • Hi,

    ich habe das folgende Problem mit einer While-Schleife:

    Die Schleife wird ausgeführt, und ist endlich - allerdings wird sie ca. 315Mio mal durchlaufen.

    Mit DoEvents habe ich es hinbekommen, dass das Programm nicht 'einfriert', aber die CPU-Last ist auf 100% (zumindest der Kern, in dem das Prog. läuft).

    Wie bekomme ich es hin, eine kurze Pause zu machen, nach einer bestimmten Anzahl an durchläufen?

    Threading.Thread.Sleep(n) möchte ich nicht nutzen, außerdem bringt das nichts für die Auslastung.

    Hier ein Teil meines Codes:

    While _array(0) <= _end + 1
    
          ikx += 1
    
    
    'Anweisungsblöcke
    'blablabla
    'blablabla
    ' ...
    
    If ikx >= 100000 Then
              Application.DoEvents()
              ikx = 0
            End If
    
    End While
    

    Vielen Dank im voraus.

     

    Sonntag, 29. Mai 2011 00:55

Antworten

  • Hallo,

    wenn Dein Laptop schlecht gekühlt wird, so lässt sich das durch Dein Programm bedingt beeinflussen.
    Auf Programmseite kannst Du am ehesten Einfluss darauf nehmen, in dem Du den Algorithmus optimierst.

    Das Betriebssystem ist darauf ausgelegt, alle anfallende Arbeit so schnell wie möglich zu erledigen
    und die verfügbare CPU-Kapazität auf alle Threads gleichmäßig zu verteilen.

    Wenn Du einen Hintergrund-Thead startest, wird das Ganze deswegen nicht langsamer (heizen)
    sondern der Thread bekommt nur dann weniger Rechenzeit zugeteilt, wenn andere Aufgaben anstehen.

    Sinnvoller im Hinblick auf die Lebenszeit des Laptops - und auf das Vermeiden von Brandblasen:
    Reduziere wenn möglich den CPU Takt im BIOS, wenn die CPU älteren Datums ist und keine
    der neueren CPU-Throttling Mechanismen unterstützt.
    Bei Windows in neueren Versionen (Vista/7) kann man über die Systemsteuerung Energieoptionen (erweitert)
    die Prozessorleistung reduzieren - was jedoch nur in Verbindung mit entsprechenden Treibern richtig funktioniert.
    Dann wird das Ganze etwas langsamer laufen, der Rechner kann aber deutlich länger leben.
    Zudem solltest Du andere Hintergrundprogramme stoppen (auch Virenscanner heizen manchmal
    ganz schön ein ;-)

    Um die Arbeit in einem Hintergrundthread anzuhalten, kannst Du dies über ein ManualResetEvent
    steuern. Üblicherweise verwendet man das, um noch nicht fertige Aufgaben zu warten.
    Es kann aber auch andersherum eingesetzt werden.

    Gruß Elmar

    • Als Antwort markiert '_OMEGA_' Montag, 30. Mai 2011 04:12
    Sonntag, 29. Mai 2011 07:46
    Beantworter

Alle Antworten

  • Die einfachste Lösung ist, die Schleife in einem Hintergrund-Thread mit niedriger Priorität laufen zu lassen.  In diesem Fall kann auf DoEvents verzichtet werden und auch mit einem Thread.Sleep die Abarbeitung der Schleife zeitweilig unterbrochen werden. Zu beachten sind ledig thread-übergreifende Zugriffe auf nicht threadsichere Objekte, wie beispielsweise Steuerelemente in der Oberfläche. Aber auch diese Probleme können einfach umgangen werden, wenn ein BackgroundWorker genutzt wird.
     
    --
    Viele Grüße
    Peter
    Sonntag, 29. Mai 2011 05:04
  • Vielen Dank für die Antwort.

    Ich habe das soweit umgesetzt, das Prog. reagiert auch gut, und die PC-Performance ist auch noch gut - aber der CPU-Kern kommt dennoch an seine Genzen.

    Im Start button habe ich jetzt:

        thread_array.IsBackground = True
        thread_array.Start()
    

    und am Ende der Schleife:

    thread_array.Abort()
    

     

    Gibt es eine Methode, mit der man den Thread oder die Schleife anhalten kann, um die CPU etwas abkühlen zu lassen?

     

    @Peter Fleischer: War ne gute Antwort, obwohl es nicht ganz das war, was ich gesucht habe.

    Hat ne andere Frage beantwortet :)

     

    '_OMEGA_'

    Sonntag, 29. Mai 2011 05:42
  • warum darf deine CPU nicht mit 100% arbeiten?

    Wenn deine CPU heiß wird, dann liegt das halt in der Natur der Sache und sollte bei korrekter Kühlung auch keine Probleme machen.

    Wenn der Thread im Hintergrund mit niedrigerer Prio läuft, stört das ja nichtmal andere Programme (wenn sie Rechenleistung benötigen, wird dein Thread einfach weggedrückt) - und man hat in der Regel sogar mehrere Kerne zur Verfügung.

    Wenn du unbedingt den Kern etwas "entlasten" willst, wird dir wohl nur ein Sleep was helfen. Der Suspended den Thread einfach und du kannst recht leicht durch die Zeit den Abkühlungsvorgang steuern... Möglich wäre leicht auch die Abfrage der Temperatur (über WMI in nem 2. Thread). Du kannst natürlich auch den Thread Suspenden und resumen - aber das musste auch von "ausserhalb" des Workerthreads machen, was es auch nicht leichter machen würde.

    Viel Spass

    Sonntag, 29. Mai 2011 05:56
  • "warum darf deine CPU nicht mit 100% arbeiten?" - Weil mein laptop (habe zur zeit leider nichts anderes zur verfügung) sehr schnell heiß wird und den ProductKey unten drunter schon halb durchgeschmoort hat.

     

    .suspend ist laut VS 2010 'veraltet' und man wird gebeten etwas anderes zu nutzen.

    Zu thread.sleep(n):

    http://social.msdn.microsoft.com/Forums/de/visualbasicde/thread/65420920-4242-4ce7-977e-90ff5def730b

    bzw.

     http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx

     

    Wahrscheinlich lasse ich das einfach so, wie es ist. Läuft ja.

    Sonntag, 29. Mai 2011 06:13
  • Also is sehe keinen Grund Sleep nicht zu nutzen. Es ist in deinem Fall vermutlich der einfachste und eleganteste Weg. Und der Artikel (hab nur den zweiten gelesen) beschreibt eher Scenarios wo Programmlogik auf Sleeps beruht - und das macht man dann vielleicht eher mit Timern. .

    Du willst ja nur die CPU zyklisch abkühlen lassen... Wenn du das alle 2 Sekunden für 2 Sekunden machst, sollte das die CPU entlasten - allerdings deine Berechnung entsprechend länger dauern lassen ^^. Ich würde längere Abkühlphasen allerdings vermeiden, da die Hardware sonst durch das ständige auf und abkühlen auch nicht besser wird... Auf wieviel % CPU Auslastung du hier kommen kannst, ohne zu "+berhitzen" kannste jaausprobieren.

    Sonntag, 29. Mai 2011 06:51
  • Hallo,

    wenn Dein Laptop schlecht gekühlt wird, so lässt sich das durch Dein Programm bedingt beeinflussen.
    Auf Programmseite kannst Du am ehesten Einfluss darauf nehmen, in dem Du den Algorithmus optimierst.

    Das Betriebssystem ist darauf ausgelegt, alle anfallende Arbeit so schnell wie möglich zu erledigen
    und die verfügbare CPU-Kapazität auf alle Threads gleichmäßig zu verteilen.

    Wenn Du einen Hintergrund-Thead startest, wird das Ganze deswegen nicht langsamer (heizen)
    sondern der Thread bekommt nur dann weniger Rechenzeit zugeteilt, wenn andere Aufgaben anstehen.

    Sinnvoller im Hinblick auf die Lebenszeit des Laptops - und auf das Vermeiden von Brandblasen:
    Reduziere wenn möglich den CPU Takt im BIOS, wenn die CPU älteren Datums ist und keine
    der neueren CPU-Throttling Mechanismen unterstützt.
    Bei Windows in neueren Versionen (Vista/7) kann man über die Systemsteuerung Energieoptionen (erweitert)
    die Prozessorleistung reduzieren - was jedoch nur in Verbindung mit entsprechenden Treibern richtig funktioniert.
    Dann wird das Ganze etwas langsamer laufen, der Rechner kann aber deutlich länger leben.
    Zudem solltest Du andere Hintergrundprogramme stoppen (auch Virenscanner heizen manchmal
    ganz schön ein ;-)

    Um die Arbeit in einem Hintergrundthread anzuhalten, kannst Du dies über ein ManualResetEvent
    steuern. Üblicherweise verwendet man das, um noch nicht fertige Aufgaben zu warten.
    Es kann aber auch andersherum eingesetzt werden.

    Gruß Elmar

    • Als Antwort markiert '_OMEGA_' Montag, 30. Mai 2011 04:12
    Sonntag, 29. Mai 2011 07:46
    Beantworter
  • MRE klingt gut.

    Habe damit vorher noch nie gearbeitet, und weiß jetzt nicht genau wie ich das machen soll.

    ich habe einen timer, der beim starten aktiviert wird.

    im timer.tick event steht folgendes

     

    Private Sub Timer_DoEvents_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer_DoEvents.Tick
      If _pause = True Then
       mre.WaitOne()
       btn_Pause.BackColor = Color.Red
       _pause = False
      ElseIf _pause = False Then
       mre.Set()    'edit: habe ich auf Reset() geändert, dadurch friert aber die anwendung ein.
       btn_Pause.BackColor = Color.Green
       _pause = True
      End If
     End Sub
    

    Edit: wenn ich das jetzt richtig verstanden habe, dann wird durch mre.Reset() der aktuelle thread geblockt. wie kann ich das auf einen anderen thread beziehen?

    intervall ist auf 1s gesetzt.

    von daher blinkt der button fröhlich vor sich hin, aber der thread wird nicht angehalten.

    muss ich MRE noch irgendwie sagen welcher thread das ist? weil ich den timer nicht in dem thread habe?

     

     

    Dim thread_array As New Threading.Thread(AddressOf array_v3)
     Private Shared mre As New ManualResetEvent(False)
    

     

    habe ich ganz oben in den deklarierungen.

    die schleife wird in "Public Sub array_v3()" ausgeführt.


    Sonntag, 29. Mai 2011 08:59
  • Hallo,

    Das Wait müsste hier im Arbeitsthread stattfinden,
    und Dein Steuerteil durch Set/Reset  das Blockieren steuern.

    Wobei es periodisch durch einen Timer anzuhalten ist letztendlich verquer.
    Wie heute morgen schon geschrieben: Versuche Deinen Rechner so einzurichten
    dass er auch unter Last funktioniert - denn jedes Überschreitern der TDP
    beschleunigt das Ableben exponential.
    Solltest Du mal Tuning-Maßnahmen vorgenommen haben, wie sie so gerne
    in den Tipps und Tricks-Ecken verbreitet werden, nimm sie zurück.

    Ich habe mal ein Beispiel in Verbindung mit einem BackgroundWorker zusammengestellt:

    Imports System.ComponentModel
    Imports System.Threading
    
    Public Class PauseWorkerForm
      Dim WithEvents worker As BackgroundWorker
    
      Dim workerPause As Boolean
      Dim workerPauseEvent As ManualResetEvent
    
      Public Sub New()
        ' 3 Buttons: startButton, pauseButton, stopButton
        ' 1 Label: progressLabel
        InitializeComponent()
    
        worker = New BackgroundWorker()
        worker.WorkerReportsProgress = True
        worker.WorkerSupportsCancellation = True
        AddHandler worker.DoWork, AddressOf Worker_DoWork
        AddHandler worker.ProgressChanged, AddressOf Worker_ProgressChanged
        AddHandler worker.RunWorkerCompleted, AddressOf Worker_RunWorkerCompleted
    
        workerPause = False
        workerPauseEvent = New ManualResetEvent(True)
    
        Me.pauseButton.Enabled = False
        Me.stopButton.Enabled = False
      End Sub
    
    #Region "Start, Pausieren, Stoppen"
      Private Sub StartWorker()
        workerPause = False
        workerPauseEvent.Set() ' Durchlässig
        worker.RunWorkerAsync()
      End Sub
    
      Private Sub PauseWorker()
        If worker.IsBusy Then
          If workerPause Then
            workerPause = False
            Me.workerPauseEvent.Set()  ' freigeben
          Else
            workerPause = True
            Me.workerPauseEvent.Reset() ' blockieren
          End If
        End If
      End Sub
    
      Private Sub StopWorker()
        ' Beendet die Arbeit
        If worker.IsBusy Then
          ' Blockierung aufheben, wenn pausiert, damit CancellationPending geprüft wird
          If workerPause Then
            workerPause = False
            Me.workerPauseEvent.Set()
          End If
          ' Abbrechen
          worker.CancelAsync()
        End If
      End Sub
    #End Region
    
    #Region "Button Ereignisse"
      Private Sub startButton_Click(sender As Object, e As System.EventArgs) _
        Handles startButton.Click
        Me.startButton.Enabled = False
        Me.pauseButton.Enabled = True
        Me.pauseButton.Text = "Pause"
        Me.stopButton.Enabled = True
    
        StartWorker()
      End Sub
    
      Private Sub pauseButton_Click(sender As Object, e As System.EventArgs) _
        Handles pauseButton.Click
        PauseWorker()
    
        If (workerPause) Then
          Me.pauseButton.Text = "Weiter"
        Else
          Me.pauseButton.Text = "Pause"
        End If
      End Sub
    
      Private Sub stopButton_Click(sender As Object, e As System.EventArgs) _
        Handles stopButton.Click
        StopWorker()
      End Sub
    
      Private Sub PauseWorkerForm_FormClosing(sender As Object, e As FormClosingEventArgs) _
        Handles MyBase.FormClosing
        ' Formular schliessen, Worker Stoppen
        StopWorker()
      End Sub
    #End Region
    
    #Region "Backgroundworker Ereignisse"
      Private Sub Worker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
        ' Arbeitschleife, hier via Random simuliert
        Const LoopCount As Integer = 1000000
        Dim r As New Random()
    
        Console.WriteLine("Worker gestartet")
    
        For index As Integer = 0 To LoopCount
          ' Wird blockiert, wenn ManualResetEvent False (Reset) ist.
          workerPauseEvent.WaitOne()
    
          ' Abbruch durch stopButton (oder Schliessen des Formulars)
          If worker.CancellationPending Then
            Console.WriteLine("Worker abgebrochen")
            e.Result = "Abgebrochen"
            Return
          End If
    
          ' ... Hier käme richtige Arbeit hin ...
          Dim value = r.Next(Integer.MaxValue)
          'Thread.Sleep(100)
    
          ' Fortschrittsanzeige
          If index Mod 1000 = 0 Then
            Dim progressPercent = index * 100 \ LoopCount
            worker.ReportProgress(progressPercent, value)
          End If
        Next
        e.Result = "Fertig"
    
        Console.WriteLine("Worker beendet")
      End Sub
    
      Private Sub Worker_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
        ' Benachrichtigung
        Dim value = CInt(e.UserState)
        Me.progressLabel.Text = String.Format("{0} % Ausgabe: {1}", e.ProgressPercentage, value)
        Me.progressLabel.Refresh()
      End Sub
    
      Private Sub Worker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
        ' Beendet entweder Arbeit fertig oder Abbgebrochen (e.Cancelled)
    
        ' Endergebnis (hier eine Zeichenkette)
        Console.WriteLine("Ergebnis " & CStr(e.Result))
    
        ' Für nächsten Durchlauf bereit
        pauseButton.Enabled = False
        pauseButton.Text = "Pause"
        stopButton.Enabled = False
        startButton.Enabled = True
      End Sub
    #End Region
    
    End Class
    

    Dort wo ich sinnfrei neue Zahlen erzeuge, käme Deine Arbeitsschleife hin.

    Gruß Elmar

    Sonntag, 29. Mai 2011 11:48
    Beantworter
  • Danke, ich habe es endlich geschafft.

    Ich habe es nicht mit einem BackgroundWorker gemacht, sondern mit einem boolean-switch im timer und den mre.set(), mre.reset() und waitone() anweisungen geschafft.

    mre.set() und mre.reset sind im timer.tick event.

    mre.waitone() ist in der arbeitsschleife.

     

    für weitere informationen, schreibt mich mich an.

     

    bitte entschuldigt die schreibweise, ich bin inner schule und hab nicht viel zeit.

     

    '_OMEGA_'

    Montag, 30. Mai 2011 01:57