Jak dynamicznie dodawać zadania do kolejki zadań PowerShell

2

Utworzyłem skrypt PowerShell, który kopiuje wszystkie e-książki, które pobieram do z góry określonego katalogu, który jest okresowo skanowany przez mojego menedżera e-booków i dodawany do mojej biblioteki. Skrypt jest uruchamiany natychmiast po każdym pobraniu.

Problem polega na tym, że gdy kilka książek jest pobieranych jednocześnie lub mniej więcej w tym samym czasie, menedżer e-booków blokuje się i przestaje odpowiadać.

Dlatego chciałbym umieścić w kolejce kopiowanie za pomocą zadań PowerShell, ale nie wiem, jak utworzyć pojedynczą kolejkę (pojedynczą współbieżność), aby każde kolejne zadanie czekało na zakończenie każdego starszego zadania.

To znaczy, chciałbym, aby skrypt utworzył zadanie (nazwijmy to „Book Job”), które okresowo sprawdza kolejkę uruchamiania Book Job, aby sprawdzić, czy wszystkie starsze Book Jobs zakończyły się przed jego uruchomieniem. Po zakończeniu zadanie Job Book powinno oświadczyć, że zostało ukończone w sposób, który może zostać wykryty przez młodsze Book Job.

Czy ktoś wie, jak to zrobić? Widziałem tutaj podobne pytanie, na które patrzę: zadania w tle PowerShell , jednak w moim przypadku uruchamiam skrypt wiele razy (po każdym nowym pobraniu).

Mavaddat Javid
źródło
1
Nie chcę tego robić (wstrzymaj skrypt i sprawdź ponownie po upływie określonego czasu), ponieważ skrypt PowerShell jest uruchamiany po zakończeniu każdego pobierania (z parametrem wejściowym dla określonego podkatalogu zawierającego nowo pobrane pliki). Oznacza to, że logika kolejkowania nie może polegać na samym skrypcie utrzymującym się w tle. Chciałbym, aby skrypt utworzył zadanie, które jest w stanie utrzymać w tle i sprawdzić, czy jest to najstarsze zadanie tego typu przed wykonaniem logiki kopiowania.
Mavaddat Javid
Czy poniższa odpowiedź wydaje się rozsądnym rozwiązaniem dla twoich potrzeb? Jestem ciekawy, czy chcesz, aby kolejka czekała na każdy znaleziony plik jeden po drugim, więc każde wykonanie skryptu powoduje utworzenie pojedynczego pliku lub tylko dla wszystkich plików, które każde wykonanie znajdzie podczas wykonywania, aby przetworzyć tylko te pliki do skopiowania lub cokolwiek innego, a nie cokolwiek dodanego do monitorowanego folderu pobierania, ponieważ po rozpoczęciu wykonywania zadania? Być może dodanie dodatkowego folderu w pętli i wyczyszczenie plików w nim przed skopiowaniem innych jest pomocne w tym procesie.
Pimp Juice IT

Odpowiedzi:

3

Moim pomysłem jest ustanowienie kolejki poprzez utworzenie jednego pliku blokady na nowe wystąpienie skryptu. Po uruchomieniu skryptu sprawdza katalog poświęcony śledzeniu kolejki dla istniejących instancji skryptu. Jeśli ich nie ma, skrypt dodaje się na początku kolejki, wykonuje akcję (uruchamia kod), a następnie czyści blokadę. Jeśli są blokady, nowa zostanie dodana na końcu kolejki, a instancja będzie bez końca sprawdzać, aż znajdzie się na początku kolejki.

Pozwala to na uruchomienie tego samego skryptu wiele razy, a każdy z nich sam sobie radzi, sprawdzając dostępną zewnętrznie kolejkę.

Pliki blokady mają strukturę indeksu, separatora („_”), identyfikatora procesu.

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

Poniżej znajduje się fikcyjny przykład na osi czasu, w którym pobrałeś trzy pliki (wybrałem losowe PID).

  • Plik 1 zostanie pobrany i uruchomi skrypt. Brak istniejących zamków. Utwórz blokadę „0_19831”. Jesteśmy na szczycie stosu, więc Twój kod jest wykonywany. To duży e-book, więc uruchomienie kodu transferu pliku zajmie pełną minutę.
  • Plik 2 zostanie pobrany i uruchomi skrypt. Istnieją blokady. Utwórz blokadę „1_332”. Nie znajdujemy się na szczycie stosu, więc poczekamy na siebie do/untili sprawdzimy, aż do pierwszego szeregu.
  • Plik 1 zakończył kopiowanie. Usuń blokadę „0_19831”.
  • Plik 3 zostanie pobrany i uruchomi skrypt. Istnieją blokady. Utwórz blokadę „2_7582”. Nie jesteśmy na szczycie stosu, poczekaj, aż będziemy.
  • Plik 2 zakończył kopiowanie. Usuń blokadę „1_332”.
  • Plik 3 zakończył kopiowanie. Usuń blokadę „2_7582”.

To rozwiązanie nie jest kuloodporne, ale może działać w zależności od skali.

korzeń
źródło