Programm wird sehr langsam ausgeführt
Hallo,
ich habe ein kleines Programm gebastelt (mit Anfängerskills!), welches erst überprüft, wieviele Benutzer angemeldet sind und wenn mehr als einer angemeldet ist, dann sollen nicht mehr als 3 Instanzen vom Programm sampleprogram.exe laufen, ansonsten dürfen 5 laufen. Und wenn gerade mehr als 3 laufen und jemand angemeldet ist, sollen die 2 ältesten (am längsten laufenden) davon beendet werden.
Ich weiß, ich habe sehr umständlich die PID eingelesen, aber besser habe ich es noch nicht hinbekommen mit meinen bisherigen Batch-Kenntnissen.
Auf jeden fall dauert dieses ganze skript immer sehr sehr lange und ich brauche etwas Hilfe, um das schneller hinzukriegen.
Hoffe das Prinzip ist klar
Danke im Voraus!
LG
Peter
ich habe ein kleines Programm gebastelt (mit Anfängerskills!), welches erst überprüft, wieviele Benutzer angemeldet sind und wenn mehr als einer angemeldet ist, dann sollen nicht mehr als 3 Instanzen vom Programm sampleprogram.exe laufen, ansonsten dürfen 5 laufen. Und wenn gerade mehr als 3 laufen und jemand angemeldet ist, sollen die 2 ältesten (am längsten laufenden) davon beendet werden.
Ich weiß, ich habe sehr umständlich die PID eingelesen, aber besser habe ich es noch nicht hinbekommen mit meinen bisherigen Batch-Kenntnissen.
Auf jeden fall dauert dieses ganze skript immer sehr sehr lange und ich brauche etwas Hilfe, um das schneller hinzukriegen.
Hoffe das Prinzip ist klar
Danke im Voraus!
LG
Peter
@echo off & SetLocal EnableDelayedExpansion
:Begin
set server=Servername
set countactive=0
set countDisconnected=0
for /f "tokens=1,2,3" %%k IN ('qwinsta /server:%server% ^| findstr "Aktiv"') DO @(
set connection=%%k
set user=%%l
set id=%%m
set /a countactive +=1
)
if !countactive! GTR 1 (
echo !date! !time! Es sind gerade !countactive! Benutzer angemeldet.
set "MaxInstances=3"
echo Deshalb sind maximale instanzen erlaubt: !MaxInstances!
if exist "prozesseCPU.txt" del "prozesseCPU.txt" /s /q >NUL
tasklist | find /i "sampleprogram.exe">>"prozesseCPU.txt"
Set /A "Counter=0"
FOR /F "delims=:" %%A IN ('findstr /N .* "prozesseCPU.txt"') DO SET /A "Counter+=1"
echo !date! !time! Es laufen gerade !Counter! Instanzen.
if !Counter! GTR 3 echo !date! !time! Das sind zu viele, deshalb wird jetzt eine beendet && goto :KillProcessCPU
goto :GoOn
) else (
set "Maxinstances=5"
echo !date! !time! Es laufen gerade !Counter! Instanzen.
echo Maximale instanzen erlaubt: !MaxInstances!
goto :GoOn
)
:: Beendet die Instanz, die schon am längsten läuft.
:KillProcessCPU
if exist "ProcessesToKillCPU.txt" del "ProcessesToKillCPU.txt" /s /q >NUL
tasklist | find /i "sampleprogram.exe">>"ProcessesToKillCPU.txt"
set /p "Prozess="<"ProcessesToKillCPU.txt"
echo !Prozess!>KillThisProcessCPU.txt
set "filename=KillThisProcessCPU.txt"
set "tempfile=KillThisProcesstmpCPU.txt"
for /F "delims=" %%a in (!filename!) do (
set text=%%a
set text=!text:sampleprogram.exe =!
set text=!text: RDP-Tcp#1=!
echo !text!>>!tempfile!
)
del !filename! >NUL
set /p "bereinigt="<"KillThisProcesstmpCPU.txt"
for /f %%i in ('type KillThisProcesstmpCPU.txt') do set KillPID=%%i & goto Out
:: Hilfsunterprogramm für KillProcess
:Out
del !tempfile! >NUL
if exist "ProcessesToKillCPU.txt" del "ProcessesToKillCPU.txt" /s /q >NUL
taskkill /f /PID !KillPID! >NUL
goto :Begin
:GoOn
echo Mehr code hier
:end
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 314602
Url: https://administrator.de/forum/programm-wird-sehr-langsam-ausgefuehrt-314602.html
Ausgedruckt am: 18.04.2025 um 07:04 Uhr
19 Kommentare
Neuester Kommentar
Moin,
eines deiner Problem ist, das du sehr viel auf Dateien zum zwischenspeichern setzt.
Du schreibst, das du Anfänger bist. Warum setzt du das Ganze dann nicht mit Powershell um und lernst dabei gleich etwas, was man in der Zukunft auch noch brauchen kann und dabei auch noch viel mächtiger ist?
Gruß Krämer
eines deiner Problem ist, das du sehr viel auf Dateien zum zwischenspeichern setzt.
Du schreibst, das du Anfänger bist. Warum setzt du das Ganze dann nicht mit Powershell um und lernst dabei gleich etwas, was man in der Zukunft auch noch brauchen kann und dabei auch noch viel mächtiger ist?
Gruß Krämer
Ich glaube, da liegst du falsch:
Beispiel Powershell für angemeldete Benutzer:
$CurrentUsers=Get-WmiObject Win32_ComputerSystem -ComputerName localhost | Select-Object UserName
Per $CurrentUsers.count kannst du die Anzahl auswerten
Beispiel Powershell für laufende cmd
$proc = Get-Process | Where-Object {$_.ProcessName -eq 'cmd'} | Select-Object id
Drum herum noch ein paar Abfragen und fertig das Ganze. Und vor allem: Viel schneller!
Gruß Krämer
Hallo,
Definiere doch mal dein sehr sehr lange. Ich kann mir da nichts drunter Vorstellen.
Hardware ist was?
OS ist was?
Prozesse laufen wie viele im Schnitt / Max?
An welche Stelle soll es denn langsam sein? Dazu mal in ein Log dir die Zeit bei einzelnen stufen notieren (echo %Time%>>%Log%). In Powershell gäbe es das "Measure-Command"
Gruß,
Peter
Definiere doch mal dein sehr sehr lange. Ich kann mir da nichts drunter Vorstellen.
Hardware ist was?
OS ist was?
Prozesse laufen wie viele im Schnitt / Max?
An welche Stelle soll es denn langsam sein? Dazu mal in ein Log dir die Zeit bei einzelnen stufen notieren (echo %Time%>>%Log%). In Powershell gäbe es das "Measure-Command"
Gruß,
Peter
Hallo,
http://ss64.com/ps/measure-command.html
Vielleicht ist ja dein KillPID ein wenig Lahm...sehe keinen wirklichen Grund warum dort Minuten vergehen, ausser dein Virtueller Server mit Windows 8 ..... oder ists der Host..... vielleicht machen deine Platten Sommerurlaub ....
Gruß,
Peter
Zitat von @Peter32:
Das mit der Log kann ich mal ausprobieren. Wie genau funktioniert das mit dem "Measure-Command"?
Powershell und https://technet.microsoft.com/de-de/library/ee176899.aspxDas mit der Log kann ich mal ausprobieren. Wie genau funktioniert das mit dem "Measure-Command"?
http://ss64.com/ps/measure-command.html
Vielleicht ist ja dein KillPID ein wenig Lahm...sehe keinen wirklichen Grund warum dort Minuten vergehen, ausser dein Virtueller Server mit Windows 8 ..... oder ists der Host..... vielleicht machen deine Platten Sommerurlaub ....
Gruß,
Peter
Moin Pjordorf,
Peter32 hat oft Zeilen wie folgende in seinem Code:
Sieht zwar umständlich, aber relativ harmlos aus? Genau hinsehen...
Gesehen? Wenn er z.B. im Root-Verzeichnis von C:\ arbeitet..
Dort existiert zufällig eine Datei "ProcessesToKillCPU.txt" ...
Dann wird danach mit
Klar kann das mal einen Moment dauern.
Grüße
Biber
Zitat von @Pjordorf:
Vielleicht ist ja dein KillPID ein wenig Lahm...sehe keinen wirklichen Grund warum dort Minuten vergehen....
ich denke, es ist relativ banal.Vielleicht ist ja dein KillPID ein wenig Lahm...sehe keinen wirklichen Grund warum dort Minuten vergehen....
Peter32 hat oft Zeilen wie folgende in seinem Code:
if exist "ProcessesToKillCPU.txt" del "ProcessesToKillCPU.txt" /s /q >NUL
.... Folgezeilen: schreib irgendwas neues in die gerade gelöschte Datei.
Sieht zwar umständlich, aber relativ harmlos aus? Genau hinsehen...
Gesehen? Wenn er z.B. im Root-Verzeichnis von C:\ arbeitet..
Dort existiert zufällig eine Datei "ProcessesToKillCPU.txt" ...
Dann wird danach mit
del dieseDatei /s /q
in allen ver###ten Unterverzeichnissen gelöscht/gesucht.Klar kann das mal einen Moment dauern.
Grüße
Biber
Moin zusammen,
ergänzend zu Bibers Kommentar: Das ganze Geraffel unter
@Peter32
Hast Du Dir diesen kruden Codeblock wirklich selbst ausgedacht oder á la Generation Google bloß irgendwelche Schnipsel aus dem Netz ohne darüber nachzudenken zusammenkopiert?
Grüße
Friemler
ergänzend zu Bibers Kommentar: Das ganze Geraffel unter
:KillProcessCPU
lässt sich außerdem auf die folgenden Zeilen eindampfen:for /f "tokens=2" %%a in ('tasklist ^| findstr /i /b /c:"sampleprogram.exe"') do (
taskkill /f /PID %%a > NUL
goto :LoopBreak
)
:LoopBreak
::Hier geht es weiter
@Peter32
Hast Du Dir diesen kruden Codeblock wirklich selbst ausgedacht oder á la Generation Google bloß irgendwelche Schnipsel aus dem Netz ohne darüber nachzudenken zusammenkopiert?
Grüße
Friemler
Hallo,
Stimmt, danke.
Gruß,
Peter
Stimmt, danke.
Gruß,
Peter
Hallo,
Gruß,
Peter
PS. Um primitive Laufzeit ergänzt.
Zitat von @Peter32:
Gibt es denn eine Möglichkeit CMD zu sagen, es soll nur in diesem einen Verzeichnis suchen? Ich habe übrigens keine Unterordner in jenem Root...
Schau dir mal ein "Del /?" an. Da wirst du den Schalter /S erklärt finden. Lasse den einfach weg.Gibt es denn eine Möglichkeit CMD zu sagen, es soll nur in diesem einen Verzeichnis suchen? Ich habe übrigens keine Unterordner in jenem Root...
Ich habe vieles zusammenkopiert
Dann sollte man sich die zeit nehmen um diese kopierten Schnipsel auch zu verstehen und vor allem was die tatsächlich tun und notfalls die einzelnen Schalter eines befehls in frage stellen.Zu deinem Code, der sehr gut funktioniert, wie kann ich da am effizientesten hinzufügen, dass er den loop so lange machen soll, bis sampleprogram.exe z.B. 5 mal noch existiert und erst dann aus dem LoopBreak?
Z.B. so in etwasetlocal enabledelayedexpansion
Set StartZeit=%Time%
REM Gesamtzähler
Set AnZahl=0
REM Zählen wie oft vorhanden
for /f "tokens=2" %%a in ('tasklist ^| findstr /i /b /c:"sampleprogram.exe"') do (
set /A AnZahl=!AnZahl!+1
Echo PID %%a ist nummer !AnZahl"
)
REM Restzähler setzen
Set /A RestAnZahl=!Anzahl!
for /f "tokens=2" %%a in ('tasklist ^| findstr /i /b /c:"sampleprogram.exe"') do (
REM Falls noch 5 oder mehr vorhanden
If !RestAnZahl! GTR 5 (
REM PID und Anzahl als Kontrolle ausgeben
echo %%a wird entfernt, !RestAnzahl! von insgesamt !AnZahl!
taskkill /f /PID %%a > NUL
REM RestAnZahl um 1 verringern
set /A RestAnZahl=!RestAnZahl!-1
)
)
goto :LoopBreak
)
:LoopBreak
::Hier geht es weiter
REM Variable Löschen
Set Anzahl=
Set RestAnZahl=
Echo Jetztzeit = %Time%
Echo StartZeit = %StartZiet%
Gruß,
Peter
PS. Um primitive Laufzeit ergänzt.
Moin Peter32,
es gibt mehrere Probleme und Folgeprobleme in deinem Batch.
Ein (ungeklärtes) Problem ist die sehr hohe Laufzeit.
Solange dafür die Ursache nicht geklärt ist, brauchen wir nicht allzuviel anders gestalten.
Was ich meine ist, wenn du jetzt in deinem Batch am Anfang ermittelst (und in irgendwelche Hilfsdateien schreibst), wieviele Prozesse XY laufen und welche PIDs die haben, der Batch dann aber 10 Minuten läuft bis du zum Killen der ältesten Prozesse kommst...
-> dann ist u.U. die ursprüngliche Information "von Prozess XY laufen 11 Instanzen mit den und den PIDs" schon längst überholt.
Sind ja jetzt vielleicht nur noch 4 Prozesse, davon 2, die es auch schon vor 10 Minuten gab und 2 neue.
Bedeutet: was du jetzt da ermittelst als Prozessliste oder in %Counter% schreibst, ist nicht oder nur von grossen Optimisten/Fatalisten verwendbar.
Daher wäre die ersten Schritte:
...in den Code. damit wir sehen, was da Zeit frisst.
Um so wichtiger ist, die Zeitfresser zu lokalisieren.
Zu den unnötigen Zwischenspeicherungen in Dateien.
... besser vom Stil her:
Deine Frage:
Grüße
Biber
es gibt mehrere Probleme und Folgeprobleme in deinem Batch.
Ein (ungeklärtes) Problem ist die sehr hohe Laufzeit.
Solange dafür die Ursache nicht geklärt ist, brauchen wir nicht allzuviel anders gestalten.
Was ich meine ist, wenn du jetzt in deinem Batch am Anfang ermittelst (und in irgendwelche Hilfsdateien schreibst), wieviele Prozesse XY laufen und welche PIDs die haben, der Batch dann aber 10 Minuten läuft bis du zum Killen der ältesten Prozesse kommst...
-> dann ist u.U. die ursprüngliche Information "von Prozess XY laufen 11 Instanzen mit den und den PIDs" schon längst überholt.
Sind ja jetzt vielleicht nur noch 4 Prozesse, davon 2, die es auch schon vor 10 Minuten gab und 2 neue.
Bedeutet: was du jetzt da ermittelst als Prozessliste oder in %Counter% schreibst, ist nicht oder nur von grossen Optimisten/Fatalisten verwendbar.
Daher wäre die ersten Schritte:
- ermittle, WO denn im Batch die Zeit verbraten wird. Also ein paar Zeilen der Form
Echo %time% bin jetzt in der For/F-Anweisung in Zeile 23 -- Laufvariable hat den Wert %%a
...in den Code. damit wir sehen, was da Zeit frisst.
Zitat von @Peter32:
Gibt es denn eine Möglichkeit CMD zu sagen, es soll nur in diesem einen Verzeichnis suchen?
Ich habe übrigens keine Unterordner in jenem Root...
Wenn du KEINE Unterverzeichnisse in denem Verzeichnis hast, dann werden der Gibt es denn eine Möglichkeit CMD zu sagen, es soll nur in diesem einen Verzeichnis suchen?
Ich habe übrigens keine Unterordner in jenem Root...
DEL /s
- nicht sovile Zeit kosten.Um so wichtiger ist, die Zeitfresser zu lokalisieren.
Zu den unnötigen Zwischenspeicherungen in Dateien.
- Tipp 1: speichere nicht soviel in Dateien (beispielsweise die tasklist-Ausgaben), sondern nimm diesen Output gleich als Parameter in den FOR/F-Anweisungen. Siehe Friemlers Beispiel.
- Tipp 2: Wenn du eine Datei erzeugst, dann immer in einem bestimmten Pfad, und wenn es eine temporäre Datei ist, dann drängt sich als Ordner der %temp%-Ordner auf
- Tipp 3: Wenn es doch eine temporäre Datei ist, dann sollst du nicht erst gucken, ob so eine vorhanden ist und die dann löschen, sondern hinterher, nach dem eigentlichen Tageswerk deines Bätsches, aufräumen und diese Trümmer löschen.
- Tipp 4: EINE bestimmte Datei in einem Ordner kannst du mit DEL ohne Parameter /s /q löschen
if exist "prozesseCPU.txt" del "prozesseCPU.txt" /s /q >NUL
tasklist | find /i "sampleprogram.exe">>"prozesseCPU.txt"
Set tmpProzesseCPU="%temp%\prozesseCPU.txt"
tasklist | find /i "sampleprogram.exe">%tmpProzesseCPU%
...
::wenn der Bätch getan hat, was ein Bätch tun kann...
del %tmpProzesseCPU%
Deine Frage:
..., wie kann ich da am effizientesten hinzufügen, dass er den loop so lange machen soll, bis sampleprogram.exe z.B. 5 mal noch existiert und erst dann aus dem LoopBreak?
-> vertagen, bis geklärt ist, was da > 10 Minuten dauert. Denn einen 10 Minuten alten %counter% können wir nicht als "skip=.."-Parameter verwenden.Grüße
Biber
Hallo,
Dann hau mit einen 12 Kilo Vorschlaghammer auf dein PC ein. Funktioniert auch irgendwie. Aber solange du ja anderer Leuts Zeit und Wissen nutzt ists ja gut...
Gruß,
Peter
Dann hau mit einen 12 Kilo Vorschlaghammer auf dein PC ein. Funktioniert auch irgendwie. Aber solange du ja anderer Leuts Zeit und Wissen nutzt ists ja gut...
Gruß,
Peter
Moin Pjordorf,
sorry, eben haben wir parallel gepostet.
Deshalb noch als Anmerkung zu deiner Zeile 16
Wenn du die noch sortierst nach PIDs...
-> dann kommen die kleinsten PIDs zuerst - also toi toi toi die ältesten, die ja zuerst gekillt werden sollen.
Ich hätte halt umgekehrt sortiert
... und noch einen skip-Parameter eingebaut mit dem Wert =%counter%-3
Grüße
Biber
sorry, eben haben wir parallel gepostet.
Deshalb noch als Anmerkung zu deiner Zeile 16
for /f "tokens=2" %%a in ('tasklist ^| findstr /i /b /c:"sampleprogram.exe"') do ( ..
Wenn du die noch sortierst nach PIDs...
for /f "tokens=2" %%a in ('tasklist ^| findstr /i /b /c:"sampleprogram.exe" ^|sort') do ( ...
-> dann kommen die kleinsten PIDs zuerst - also toi toi toi die ältesten, die ja zuerst gekillt werden sollen.
Ich hätte halt umgekehrt sortiert
for /f "tokens=2" %%a in ('tasklist ^| findstr /i /b /c:"sampleprogram.exe" ^|sort /reverse') do ( ...
... und noch einen skip-Parameter eingebaut mit dem Wert =%counter%-3
Grüße
Biber
Hallo Biber,
da muss ich jetzt wohl mal in die Bresche springen, die kritisierte Zeile stammt ja schließlich von mir.
Die von Dir angesprochene Befürchtung nach dem Alter der Prozesse kam mir schon, als ich den Code von Peter32 nachvollzogen habe (dort ist der Teil innerhalb der
Ich persönlich hätte das (auf etwas sicherere Art und Weise) auch eher als Unterprogramm implementiert, dass man mit
So, und jetzt hat unser TO wieder einen Schnipsel zum kopieren...
Gruß
Friemler
da muss ich jetzt wohl mal in die Bresche springen, die kritisierte Zeile stammt ja schließlich von mir.
Die von Dir angesprochene Befürchtung nach dem Alter der Prozesse kam mir schon, als ich den Code von Peter32 nachvollzogen habe (dort ist der Teil innerhalb der
FOR
-Klammer bereits enthalten). Meine Tests haben jedoch ergeben, dass der TASKLIST
-Befehl implizit (und undokumentiert) wohl tatsächlich die Prozesse nach ihrem Alter sortiert anzeigt (älteste zuerst). Das System vergibt PIDs auch nicht in aufsteigender Reihenfolge, d.h. ein Prozess kann durchaus eine kleinere PID haben als die vor ihm gestarteten Prozesse.Ich persönlich hätte das (auf etwas sicherere Art und Weise) auch eher als Unterprogramm implementiert, dass man mit
call :KillProcess
aufruft (ungetestet)::KillProcess
setlocal enabledelayedexpansion
set "CreationDate="
for /f "tokens=2 delims==" %%a in ('wmic process where "name='sampleprogram.exe'" get creationdate /value') do (
if not defined CreationDate (
set "CreationDate=%%a"
) else if "%%a" lss "!CreationDate!" (
set "CreationDate=%%a"
)
)
if defined CreationDate (
wmic process where "name='sampleprogram.exe' and creationdate='%CreationDate%'" call terminate > NUL
)
endlocal
exit /b 0
So, und jetzt hat unser TO wieder einen Schnipsel zum kopieren...
Gruß
Friemler
Moin Friemler,
danke für die Info - wusste ich nicht, dass tasklist die Prozesse nach Alter ausgibt.
Ich hatte auch nur mal kurz angetestet mit "svchost.exe" - der Prozesse läuft ja immer ein Dutzend mal- und gesehen, dass die Reihenfloge der PIDs nicht aufsteigend ist.
Nachvollziehbar wäre für mich gewesen, wenn die Redmonder PraktikantInnen jeweils eine neue PID aus einer Sequence o.ä. ermittelten - also immer eine höhere PID als schon mal vergeben.
Gibt aber anscheinend für die echten Profis auch andere Implementierungsmöglichkeiten.
Grüße
Biber
danke für die Info - wusste ich nicht, dass tasklist die Prozesse nach Alter ausgibt.
Ich hatte auch nur mal kurz angetestet mit "svchost.exe" - der Prozesse läuft ja immer ein Dutzend mal- und gesehen, dass die Reihenfloge der PIDs nicht aufsteigend ist.
Nachvollziehbar wäre für mich gewesen, wenn die Redmonder PraktikantInnen jeweils eine neue PID aus einer Sequence o.ä. ermittelten - also immer eine höhere PID als schon mal vergeben.
Gibt aber anscheinend für die echten Profis auch andere Implementierungsmöglichkeiten.
Grüße
Biber