Start-Job
将一个脚本块发送到一个新的 PowerShell 进程,这样它可以独立并行运行。以下是一个非常简单的例子,演示 job 背后的概念:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| $job1 = { Start-Sleep -Seconds 6 ; 1 } $job2 = { Start-Sleep -Seconds 8 ; 2 } $job3 = { Start-Sleep -Seconds 5 ; 3 }
$j1 = Start-Job -ScriptBlock $job1 $j3 = Start-Job -ScriptBlock $job3
$ej2 = & $job2
$null = Wait-Job -Job $J1, $j3
$ej1 = Receive-Job -Job $j1 $ej3 = Receive-Job -Job $j3 Remove-Job -Job $j1, $j3
$ej1, $ej2, $ej3
|
如果不用 job,那么需要等待 19 秒。幸好有了 job,这个过程可以缩短到 8 秒。
然而,也有副作用。由于 job 是在独立的应用中执行的,数据必须以 XML 序列化的方式来回传递。job 要传回越多的数据,就需要越多的时间。有些时候这个副作用会盖过了优点。
一个更好的方是在原来的 PowerShell 实例的子线程中运行 job。以下代码演示这种功能。它创建了一个新的名为 Start-MemoryJob
的命令,可以替代 Start-Job
。其余的代码完全不用改变。
使用 Start-MemoryJob
,不需要任何对象序列化。您的 job 可以快速平滑地运行,而没有返回大量的数据。而且,您现在获取到的是原始的对象。不再需要处理序列化过的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
| $code = @' using System; using System.Collections.Generic; using System.Text; using System.Management.Automation; using System.Management.Automation.Runspaces; namespace InProcess { public class InMemoryJob : System.Management.Automation.Job { public InMemoryJob(ScriptBlock scriptBlock, string name) { _PowerShell = PowerShell.Create().AddScript(scriptBlock.ToString()); SetUpStreams(name); } public InMemoryJob(PowerShell PowerShell, string name) { _PowerShell = PowerShell; SetUpStreams(name); } private void SetUpStreams(string name) { _PowerShell.Streams.Verbose = this.Verbose; _PowerShell.Streams.Error = this.Error; _PowerShell.Streams.Debug = this.Debug; _PowerShell.Streams.Warning = this.Warning; _PowerShell.Runspace.AvailabilityChanged += new EventHandler<RunspaceAvailabilityEventArgs>(Runspace_AvailabilityChanged); int id = System.Threading.Interlocked.Add(ref InMemoryJobNumber, 1); if (!string.IsNullOrEmpty(name)) { this.Name = name; } else { this.Name = "InProcessJob" + id; } } void Runspace_AvailabilityChanged(object sender, RunspaceAvailabilityEventArgs e) { if (e.RunspaceAvailability == RunspaceAvailability.Available) { this.SetJobState(JobState.Completed); } } PowerShell _PowerShell; static int InMemoryJobNumber = 0; public override bool HasMoreData { get { return (Output.Count > 0); } } public override string Location { get { return "In Process"; } } public override string StatusMessage { get { return "A new status message"; } } protected override void Dispose(bool disposing) { if (disposing) { if (!isDisposed) { isDisposed = true; try { if (!IsFinishedState(JobStateInfo.State)) { StopJob(); } foreach (Job job in ChildJobs) { job.Dispose(); } } finally { base.Dispose(disposing); } } } } private bool isDisposed = false; internal bool IsFinishedState(JobState state) { return (state == JobState.Completed || state == JobState.Failed || state == JobState.Stopped); } public override void StopJob() { _PowerShell.Stop(); _PowerShell.EndInvoke(_asyncResult); SetJobState(JobState.Stopped); } public void Start() { _asyncResult = _PowerShell.BeginInvoke<PSObject, PSObject>(null, Output); SetJobState(JobState.Running); } IAsyncResult _asyncResult; public void WaitJob() { _asyncResult.AsyncWaitHandle.WaitOne(); } public void WaitJob(TimeSpan timeout) { _asyncResult.AsyncWaitHandle.WaitOne(timeout); } } } '@ Add-Type -TypeDefinition $code function Start-JobInProcess { [CmdletBinding()] param ( [scriptblock] $ScriptBlock, $ArgumentList, [string] $Name ) function Get-JobRepository { [cmdletbinding()] param() $pscmdlet.JobRepository } function Add-Job { [cmdletbinding()] param ( $job ) $pscmdlet.JobRepository.Add($job) } if ($ArgumentList) { $PowerShell = [PowerShell]::Create().AddScript($ScriptBlock).AddArgument($argumentlist) $MemoryJob = New-Object InProcess.InMemoryJob $PowerShell, $Name } else { $MemoryJob = New-Object InProcess.InMemoryJob $ScriptBlock, $Name } $MemoryJob.Start() Add-Job $MemoryJob $MemoryJob }
|