peter32
Goto Top

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 face-smile

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

Content-Key: 314602

Url: https://administrator.de/contentid/314602

Printed on: April 20, 2024 at 00:04 o'clock

Member: Kraemer
Kraemer Sep 07, 2016 at 11:08:47 (UTC)
Goto Top
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
Member: Peter32
Peter32 Sep 07, 2016 at 12:26:45 (UTC)
Goto Top
Hallo Krämer,

weil ich glaube, dass bevor ich PowerShell lerne, ich besser erstmal Batch verstehen sollte, weil mir das einfacher erscheint.

Gruß
Peter
Member: Kraemer
Kraemer Sep 07, 2016 at 12:47:51 (UTC)
Goto Top
Zitat von @Peter32:
weil mir das einfacher erscheint.

Ich glaube, da liegst du falsch:

Beispiel Powershell für angemeldete Benutzer:
$CurrentUsers=Get-WmiObject Win32_ComputerSystem -ComputerName localhost | Select-Object UserName
$CurrentUsers ist ein Array der Benutzernamen
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  
$proc ist ein Array der PID's

Drum herum noch ein paar Abfragen und fertig das Ganze. Und vor allem: Viel schneller!

Gruß Krämer
Member: Pjordorf
Pjordorf Sep 07, 2016 at 12:48:45 (UTC)
Goto Top
Hallo,

Zitat von @Peter32:
Auf jeden fall dauert dieses ganze skript immer sehr sehr lange
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
Member: Peter32
Peter32 Sep 07, 2016 updated at 17:14:59 (UTC)
Goto Top
Danke für die Antworten!

@Pjordorf:
Mit sehr sehr lange meine ich ein paar Minuten.
Hardware ist ein Virtueller Server mit Windows 8 und 4 Kernen mit 2,2GHz und 16GB RAM.
Im Schnitt laufen um die 100 Prozesse.
Das mit der Log kann ich mal ausprobieren. Wie genau funktioniert das mit dem "Measure-Command"?

@krämer, du hast geschrieben:

"Drum herum noch ein paar Abfragen und fertig das Ganze. Und vor allem: Viel schneller!"  

Wenn es scheinbar nicht so aufwändig in PowerShell ist und du dazu Zeit hast, würde ich mich natürlich über eine Demonstration deiner PowerShell Künste sehr freuen face-smile

LG
Peter
Member: Pjordorf
Pjordorf Sep 07, 2016 at 17:28:28 (UTC)
Goto Top
Hallo,

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.aspx
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
Member: Biber
Biber Sep 08, 2016 updated at 07:10:18 (UTC)
Goto Top
Moin Pjordorf,


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.
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
Member: Friemler
Solution Friemler Sep 08, 2016 updated at 08:46:58 (UTC)
Goto Top
Moin zusammen,

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
Member: Pjordorf
Pjordorf Sep 08, 2016 at 10:04:25 (UTC)
Goto Top
Hallo,

Zitat von @Biber:
Sieht zwar umständlich, aber relativ harmlos aus? Genau hinsehen...
Stimmt, danke.

Gruß,
Peter
Member: Peter32
Peter32 Sep 08, 2016 at 12:55:12 (UTC)
Goto Top
Hallo,

Danke für Eure Beiträge!


Zitat von @Biber:
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.

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...


Zitat von @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?

Ich habe vieles zusammenkopiert aber auch selbst überlegt, wie ich an die einzelnen Ziele komme mit meinen bisherigen Batch-Kenntnissen.
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?
Member: Pjordorf
Pjordorf Sep 08, 2016 updated at 14:57:48 (UTC)
Goto Top
Hallo,

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.

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 etwa
setlocal 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.
Member: Peter32
Peter32 Sep 08, 2016 at 14:21:18 (UTC)
Goto Top
Super Danke - das werde ich gleich ausprobieren face-smile


Zitat von @Pjordorf:

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.


Ja das versuche ich ja auch und werde ja auch langsam Stück für Stück besser, aber ich will ja auch kein Profi werden, sondern es soll nur dem Zweck dienen und auch wenn es nicht perfekt geschrieben ist, solange es funktioniert, bin ich schon zufrieden face-smile
Member: Biber
Biber Sep 08, 2016 updated at 14:41:12 (UTC)
Goto Top
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:
  • 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 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
Also statt bisher:
	if exist "prozesseCPU.txt" del "prozesseCPU.txt" /s /q >NUL  
	tasklist | find /i "sampleprogram.exe">>"prozesseCPU.txt"  
... besser vom Stil her:
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
Member: Pjordorf
Pjordorf Sep 08, 2016 at 14:34:47 (UTC)
Goto Top
Hallo,

Zitat von @Peter32:
solange es funktioniert, bin ich schon zufrieden face-smile
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... face-smile

Gruß,
Peter
Member: Biber
Biber Sep 08, 2016 at 14:48:19 (UTC)
Goto Top
Moin Pjordorf,

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
Member: Friemler
Friemler Sep 08, 2016 updated at 16:29:18 (UTC)
Goto Top
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 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... face-wink

Gruß
Friemler
Member: Peter32
Peter32 Sep 08, 2016 at 15:14:17 (UTC)
Goto Top
Hallo Biber, vielen Dank für deinen Beitrag!


Zitat von @Biber:
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.



Dieser Code funktioniert leider nicht. Wenn ich das "^|sort" dazunehme, liest er plötzlich keine einzige Anzahl mehr aus.
Ich würde aber doch ganz gerne eben den ältesten Prozess beenden...

Du hast etwas mit %counter%-3 erwähnt, kannst du das bitte noch einmal etwas genauer erklären oder an einem Beispiel klar machen?

Danke im Voraus!

Liebe Grüße
Peter
Member: Biber
Biber Sep 08, 2016 updated at 15:25:52 (UTC)
Goto Top
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. face-wink

Grüße
Biber
Member: Biber
Biber Sep 08, 2016 updated at 15:24:35 (UTC)
Goto Top
Upps, Peter32,

ich habe ein Anführungszeichen nach dem Wort sort zuviel.bzw an der falschen Stelle.. das dürfte das Problem sein.
Aber bitte lies erst Friemlers Kommentar - meine Strategie ist hier ohnehin falsch..
In meinem Kommentar an den Pjordorf-Peter korrigiere ich es gleich.

Grüße
Biber