So fügen Sie Aufgaben dynamisch zu einer PowerShell-Jobwarteschlange hinzu

499
Mavaddat Javid

Ich habe ein PowerShell-Skript erstellt, das alle von mir heruntergeladenen E-Books in ein vorbestimmtes Verzeichnis kopiert. Dieses E-Book-Manager wird regelmäßig gescannt und meiner Bibliothek hinzugefügt. Das Skript wird sofort nach jedem Download ausgeführt.

Das Problem ist, dass der E-Book-Manager blockiert und nicht mehr reagiert, wenn mehrere Bücher gleichzeitig oder ungefähr zur gleichen Zeit heruntergeladen werden.

Daher würde ich gerne das Kopieren mit PowerShell-Jobs in eine Warteschlange stellen, aber ich weiß nicht, wie eine einzelne Warteschlange (Single Concurrency) erstellt wird, die für jeden nachfolgenden Job die Fertigstellung jedes älteren Jobs erwartet.

Das heißt, ich möchte, dass das Skript einen Job erstellt (nennen wir es einen "Book-Job"), der die Warteschlange der ausgeführten Book-Jobs regelmäßig überprüft, um festzustellen, ob alle älteren Book-Jobs abgeschlossen sind, bevor sie ausgeführt werden. Wenn er abgeschlossen ist, sollte ein Buchauftrag angeben, dass er auf eine Weise beendet wurde, die von jüngeren Buchaufträgen erkannt werden kann.

Weiß jemand, wie ich das machen kann? Ich habe hier eine ähnliche Frage gesehen: Powershell-Hintergrundaufgaben . In meinem Fall führe ich das Skript jedoch mehrmals (nach jedem neuen Download) aus.

3
Ich möchte das nicht tun (das Skript sollte sich im Ruhezustand befinden und nach einem Intervall erneut prüfen), da das PowerShell-Skript nach Abschluss jedes Downloads ausgeführt wird (mit einem Eingabeparameter für das bestimmte Unterverzeichnis, das die neu heruntergeladenen Dateien enthält). Dies bedeutet, dass sich die Warteschlangenlogik nicht darauf verlassen kann, dass das Skript selbst im Hintergrund verbleibt. Ich möchte, dass das Skript einen Job erstellt, der im Hintergrund bestehen bleibt und vor dem Ausführen der Kopierlogik überprüft, ob es sich um den ältesten Job seiner Art handelt. Mavaddat Javid vor 6 Jahren 1
Sieht die nachstehende Antwort als eine vernünftige Lösung für Ihre Bedürfnisse aus? Ich bin neugierig, ob die Warteschlange auf jede gefundene Datei warten soll, so dass jede Ausführung des Skripts eine einzelne Datei oder nur die Dateien ausführt, die bei der Ausführung gefunden werden, um nur die zu kopierenden Dateien zu verarbeiten oder was auch immer und nicht Was wurde dem überwachten Download-Ordner hinzugefügt, seit der Job seine Ausführung gestartet hat? Das Hinzufügen eines zusätzlichen Ordners in der Schleife und das Löschen der Dateien darin, bevor andere kopiert werden, kann bei diesem Vorgang hilfreich sein. Pimp Juice IT vor 6 Jahren 0

1 Antwort auf die Frage

3
root

Mein Gedanke ist, eine Warteschlange einzurichten, indem für jede neue Instanz Ihres Skripts eine Sperrdatei erstellt wird. Bei der Ausführung des Skripts wird ein Verzeichnis geprüft, in dem die Warteschlange nach vorhandenen Instanzen des Skripts gesucht wird. Wenn keine vorhanden ist, fügt sich das Skript vor der Warteschlange ein, führt eine Aktion aus (führt den Code aus) und bereinigt dann die Sperre. Wenn es Sperren gibt, wird am Ende der Warteschlange eine neue hinzugefügt, und die Instanz prüft endlos, bis sie sich am Anfang der Warteschlange befindet.

Auf diese Weise können Sie dasselbe Skript mehrmals ausführen, wobei alle einzeln durch Prüfen der extern verfügbaren Warteschlange selbst behandelt werden.

Die Sperrdateien sind als Index, Trennzeichen ("_") und Prozess-ID strukturiert.

Clear-Host  function New-Lock ([int] $index) { $newLock = "$index" + "_" + $pid + ".lck" New-Item $queue$newLock | Out-Null }  $queue = "C:\locks\"  # find the end of the stack $locks = gci $queue *.lck | sort | select -expandproperty name  # if locks exist, find the end of the stack by selecting the index of the last lock if($locks) { # gets the last lock file, selects the index by splitting on the delimiter [int]$last = [convert]::ToInt32(($locks | select -last 1).Split("_")[0],10)  # add the lock to the end of the stack New-Lock ($last + 1) } # if no locks exist, create one at the top of the stack else { New-Lock 0 }  # check if we're at the top of the stack do { $locks = gci $queue *.lck | sort | select -expandproperty name  # this is the PID on the top of the stack [int]$top = [convert]::ToInt32(($locks | select -first 1).Split("_")[1].Split(".")[0],10) write-verbose "not at the top..." sleep 1 } until ($pid -eq $top)  # if we're here, we've been to the top. it's our turn to do something Write-Verbose "we've reached the top!" # <do something. put your code here> # might be good to add some Start-Sleep here # </do something put your code here>  # now that we're done, let's delete our lock gci $queue | select -first 1 | Remove-Item 

Nachfolgend finden Sie ein Beispiel für eine fiktive Zeitleiste, in dem Sie drei Dateien heruntergeladen haben (ich habe zufällige PIDs ausgewählt).

  • Datei 1 wird heruntergeladen und startet das Skript. Es sind keine Sperren vorhanden. Sperre "0_19831" erstellen. Wir sind an der Spitze des Stapels, also wird Ihr Code ausgeführt. Dies ist ein großes E-Book, daher dauert die Ausführung Ihres Dateiübertragungscodes eine volle Minute.
  • Datei 2 wird heruntergeladen und startet das Skript. Sperre (n) existieren Sperre "1_332" erstellen. Wir sind nicht an der Spitze des Stapels, also warten wir in unserem do/untilund warten weiter, bis wir an erster Stelle stehen.
  • Datei 1 ist fertig kopiert. Lösche die Sperre "0_19831".
  • Datei 3 wird heruntergeladen und startet das Skript. Sperre (n) existieren Sperren "2_7582" erstellen. Wir sind nicht an der Spitze des Stapels, warten Sie, bis wir es sind.
  • Datei 2 ist fertig kopiert. Lösche die Sperre "1_332".
  • Datei 3 ist fertig kopiert. Lösche die Sperre "2_7582".

Diese Lösung ist nicht kugelsicher, kann jedoch je nach Skalierung funktionieren.