4

我有一台带有很多媒体驱动器的服务器 ~43TB。一个 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 }

这只是要求阵列中的每个驱动器都列出其根文件夹名称。这可以唤醒驱动器,但它又是一个线性函数。该脚本在打印前为每个驱动器暂停。寻找有关如何同时唤醒每个驱动器的解决方案。有没有办法多线程或其他?

4

4 回答 4

2

这是一个可以执行您想要的操作的脚本,但它必须使用 MTA 线程模式在 powershell 下运行(这是 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 作为并行度因素——用更低或更高的数字来试验自己。

于 2012-12-27T17:39:18.107 回答
1

Spin up a separate powershell instance for each drive or use workflows in PowerShell 3.0. Anyhow, you can pass drives directly to the Path parameter and skip Foreach-Object all togeteher:

Get-ChildItem $mediaDrives
于 2012-12-27T10:02:16.150 回答
1

您是否考虑过使用Start-Job cmdlet 来解决这个问题:

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

唯一聪明的部分是您需要在 Start-Job cmdlet 上使用 -ArgumentList 参数来为每次迭代传递正确的值。这将创建一个与脚本执行并行运行的后台任务。如果你很好奇

于 2013-01-01T22:01:29.493 回答
0

如果您不想等待,那就不要等待:在后台启动那些叫醒电话。

bash一个会写

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

(注意与号,意思是:在后台启动命令,不要等待它完成

在 PowerShell 中,这将转化为类似

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

如果您想确认所有后台作业都已完成,请在waitPowershell中使用bashWait-Job

于 2012-12-31T11:38:23.630 回答