Powershell для одновременного пробуждения нескольких накопителей

У меня есть сервер с большим количеством медиа-дисков ~ 43 ТБ. Areca 1882ix-16 настроен на остановку дисков после 30 минут бездействия, поскольку в большинстве дней отдельный диск даже не используется. Это хорошо работает, чтобы предотвратить ненужную мощность и тепло. В этом случае диски по-прежнему отображаются в проводнике Windows, но когда вы щелкаете, чтобы получить к ним доступ, для отображения списка папок требуется около 10 секунд, поскольку он должен ждать, пока диск раскрутится.

Для административной работы мне нужно раскрутить все диски, чтобы иметь возможность искать среди них. Нажимать на каждый диск в проводнике Windows, а затем ждать, пока он раскрутится, прежде чем щелкнуть следующий диск, очень утомительно. Очевидно, что несколько окон проводника делают его быстрее, но все равно утомительно. Я думал, что сценарий powershell может облегчить боль.

Итак, я начал со следующего:

$mediaDrives = @('E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:', 'L:',
    'M:','N:', 'O:', 'P:', 'Q:', 'R:', 'S:')
get-childitem $mediaDrives  | foreach-object -process { $_.Name }

Это просто запрос, чтобы у каждого диска в массиве было указано имя его корневой папки. Это работает, чтобы разбудить диск, но это снова линейная функция. Сценарий приостанавливается для каждого диска перед печатью. Ищете решение, как разбудить каждый диск одновременно. Есть ли способ многопоточности или что-то еще?


person Dan    schedule 27.12.2012    source источник


Ответы (4)


Вот скрипт, который будет делать то, что вы хотите, но его нужно запускать под powershell с использованием многопоточного режима MTA (который используется по умолчанию для powershell.exe 2.0, но powershell.exe 3.0 нужно запускать с ключом -MTA).

#require -version 2.0

# if running in ISE or in STA console, abort
if (($host.runspace.apartmentstate -eq "STA") -or $psise) {
    write-warning "This script must be run under powershell -MTA"
    exit
}

$mediaDrives = @('E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:', 'L:',
    'M:','N:', 'O:', 'P:', 'Q:', 'R:', 'S:')

# create a pool of 8 runspaces   
$pool = [runspacefactory]::CreateRunspacePool(1, 8)
$pool.Open()

$jobs = @()
$ps = @()
$wait = @()     

$count = $mediaDrives.Length

for ($i = 0; $i -lt $count; $i++) {

    # create a "powershell pipeline runner"
    $ps += [powershell]::create()  

    # assign our pool of 8 runspaces to use
    $ps[$i].runspacepool = $pool   

    # add wake drive command
    [void]$ps[$i].AddScript(
        "dir $($mediaDrives[$i]) > `$null")

    # start script asynchronously    
    $jobs += $ps[$i].BeginInvoke();         

    # store wait handles for WaitForAll call
    $wait += $jobs[$i].AsyncWaitHandle
}

# wait 5 minutes for all jobs to finish (configurable)
$success = [System.Threading.WaitHandle]::WaitAll($wait,
    (new-timespan -Minutes 5))     
write-host "All completed? $success"

# end async call   
for ($i = 0; $i -lt $count; $i++) {     
    write-host "Completing async pipeline job $i"    

    try {       
        # complete async job           
        $ps[$i].EndInvoke($jobs[$i])     
    } catch {   
        # oops-ee!          
        write-warning "error: $_"      
    }   

    # dump info about completed pipelines       
    $info = $ps[$i].InvocationStateInfo

    write-host "State: $($info.state) ; Reason: $($info.reason)"  
}

Так, например, сохраните как warmup.ps1 и запустите как: powershell -mta c:\scripts\warmup.ps1

Чтобы узнать больше о пулах пространства выполнения и описанном выше общем методе, взгляните на мою запись в блоге о пулах пространства выполнения:

http://nivot.org/blog/post/2009/01/22/CTP3TheRunspaceFactoryAndPowerShellAccelerators

Я выбрал 8 довольно произвольно из-за фактора параллелизма — поэкспериментируйте сами с меньшими или большими числами.

person x0n    schedule 27.12.2012
comment
Спасибо, что собрал это для меня. Тем не менее, на самом деле, кажется, что диски не раскручиваются параллельно. Для завершения сценария и запуска всех дисков требуется от 2,5 до 3 минут. Интересно, лучше ли это сделать, написав приложение на С#. Кажется, что многопоточность - это не то, что Powershell делает так хорошо. Если у вас есть какие-либо указания по написанию приложения С# для этого, дайте мне знать. - person Dan; 28.12.2012
comment
может просто так долго? попробуйте открыть одно окно консоли (команда работает быстро) для каждого диска и подготовьте каждое окно, введя в каждое по одному идентификатору диска, затем нажмите Alt Tab, чтобы запустить их все, нажав Enter. время это. - person x0n; 29.12.2012

Разверните отдельный экземпляр PowerShell для каждого диска или используйте рабочие процессы в PowerShell 3.0. В любом случае, вы можете передать диски напрямую в параметр Path и вообще пропустить Foreach-Object:

Get-ChildItem $mediaDrives
person Shay Levy    schedule 27.12.2012

Рассматривали ли вы вариант с помощью командлета Start-Job:

$mediaDrives = @('E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:')
$mediaDrives | ForEach-Object {
  Start-Job -ArgumentList $_ -ScriptBlock {param($drive)
    Get-ChildItem $drive
  }
}

Единственная умная часть заключается в том, что вам нужно использовать параметр -ArgumentList в командлете Start-Job, чтобы передавать правильное значение для каждой итерации. Это создаст фоновую задачу, которая запускается параллельно с выполнением скрипта. Если вам интересно

person Goyuix    schedule 01.01.2013

Если вы не хотите ждать, не ждите: запустите эти тревожные звонки в фоновом режиме.

В bash можно было бы написать

foreach drive ($mediadrives) {tickle_and_wake $drive &}

(обратите внимание на амперсанд, который означает: запустить команду в фоновом режиме, не дожидаясь ее завершения)

В PowerShell это будет означать что-то вроде

foreach ($drive in $mediadrives)  {
   Start-Job {param($d) tickle_and_wake $d} -Arg $drive 
}

Если вы хотите подтвердить, что все фоновые задания завершены, используйте wait в bash или Wait-Job в Powershell.

person Hans Lub    schedule 31.12.2012