멀티 태스킹으로 몇가지 테크닉을 작성한 적이 있습니다만,
그 중에서도 runspace가 가장 상위에 있는 개념이지 않을까 합니다.
runspace는 그냥 하나의 다른 powershell을 띄워놓고 실행하고 있다고 생각하시면 됩니다. thread나 job보다도 가장 상위의 개념이라고 할 수 있습니다. 환경변수도 따로 가지고 있고 공유하는 것이 없습니다. 편하게, powershell창 하나 더 띄워서 실행하는 것이라고 생각하세요.
기본 포맷
실행가능한 가장 간단한 형태를 갖춘 코드는 다음과 같습니다. ref의 Beginning Use of PowerShell Runspaces: Part 1
에서 가져온 코드입니다.
1
2
3
4
5
6
7
8
| $Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.runspace = $Runspace
$Runspace.Open()
[void]$PowerShell.AddScript({
Get-Date
})
$PowerShell.Invoke()
|
하지만, 기본 사용법은 기본 사용법일 뿐입니다. 여기에 pool기능을 넣으면 다음과 같은 코드가 됩니다. 이 코드는 ref의 Beginning Use of PowerShell Runspaces: Part 3
에서 가져온 코드입니다.
1
2
3
4
5
6
7
8
9
10
11
| [runspacefactory]::CreateRunspacePool()
$SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$RunspacePool = [runspacefactory]::CreateRunspacePool(
1, #Min Runspaces
5 #Max Runspaces
)
$PowerShell = [powershell]::Create()
#Uses the RunspacePool vs. Runspace Property
#Cannot have both Runspace and RunspacePool property used; last one applied wins
$PowerShell.RunspacePool = $RunspacePool
$RunspacePool.Open()
|
pool은 동시에 실행되는runspace의 환경입니다. ThrottleLimit와 비슷한 개념으로 사용합니다.
실전에서 사용하려면 좀 더 체계를 갖춰야 합니다.
체계를 갖춘 포맷
다음의 포맷이 현재 실전에서 사용하고 있는 포맷입니다. 크게
- 코드부분
- runspace를 생성하는 부분
- runspace 종료를 기다리고 결과를 출력하는 부분
으로 나뉩니다.
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
|
Function Invoke-MultiRunspace {
[CmdletBinding()]
Param(
[Parameter(HelpMessage="the max pool size.")]
[Int]$PoolMaxSize = (Get-WmiObject Win32_Processor).NumberOfLogicalProcessors
)
Begin {
$Date = (get-date).Tostring("HHmmss")
$buffer = [System.Collections.ArrayList]::new()
Write-Host @"
########################################
PoolMaxSize: $PoolMaxSize
########################################
"@
$target_count = 100
if ($target_count -gt 0) {
$answer = Read-Host "Run Script [Y/N]"
if (($answer -ne "Y")) {
Write-Host "script exit."
# use exit or continue to stop this script
# return will proceed Process block
continue
}
} else {
Write-Host "no target detected."
Continue
}
}
Process {
$now=Get-Date -format "yyyy/MM/dd HH:mm:ss"
$fileFormat = Get-Date -format "dd-MMM-yy_HHmmss"
Write-Host "Script Start : '$($now)'" -ForegroundColor Yellow
$global:SourceCount = 0 ### To know the total count of the documents to be processed
$global:Processed = 0
Write-Host "Calculating ..." -ForegroundColor White
$count_failure = 0
$count_success = 0
$num = 0
# 이부분이 각각의 runspace에서 동작하는 코드의 메인입니다.
$Code = {
# mod is the number given for the thread
Param($some_parameter)
try {
Write-Host "$num. starting $(Get-Date -Format "yyyy/MM/dd HH:mm:ss")"
[System.Console]::WriteLine( "transaction starting $(Get-Date -Format "yyyy/MM/dd HH:mm:ss")" )
# doing long transaction
Start-Sleep -s (Get-Random -Minimum 99 -Maximum 199)
[System.Console]::WriteLine( "transaction completed $(Get-Date -Format "yyyy/MM/dd HH:mm:ss")" )
"success"
}
catch {
"failed: $_"
exit
}
finally {
}
}
try {
$RunSpacePool = [runspacefactory]::CreateRunspacePool(1, $PoolMaxSize)
$RunSpacePool.ApartmentState = "STA"
$RunSpacePool.Open()
# 이 부분이 각각의 runspace를 생성하는 부분입니다.
# create runspaces
$RunspaceCollection = New-Object System.Collections.ArrayList
foreach ($i in 1..99) {
$PSInstance = [powershell]::Create().AddScript($Code).AddArgument($i)
$PSInstance.RunSpacePool = $RunSpacePool
[void]$RunspaceCollection.Add([PSCustomObject]@{
Runspace = $PSInstance.BeginInvoke()
powershell = $PSInstance
})
}
# detect the thread finish
$ErrorItems = New-Object System.Collections.ArrayList
while ($RunspaceCollection.Count -gt 0) {
for ($i = 0; $i -lt $RunspaceCollection.Count; $i++) {
if ($RunspaceCollection[$i].Runspace.IsCompleted) {
$output = $RunspaceCollection[$i].powershell.EndInvoke($RunspaceCollection[$i].Runspace)
Write-Debug "$output"
if (($output | ConvertTo-Json).Contains("failed")) {
[void]$ErrorItems.Add($RunspaceCollection[$i].node)
$count_failure ++
} else {
$count_success ++
}
$RunspaceCollection[$i].powershell.Dispose()
$RunspaceCollection.RemoveAt($i)
}
}
Start-Sleep -Milliseconds 100
}
}
finally {
$RunSpacePool.Close()
}
$now=Get-Date -format "yyyy/MM/dd HH:mm:ss"
Write-Host "Total Count: 100 Completed: $($count_success) Failure: $count_failure" -ForegroundColor Cyan
Write-Host "END Start : '$($now)'" -ForegroundColor Yellow
}
}
|
필요하실 때, Code부분과 그에 넘기는 파라메터만 정의해서 사용하시면 될 듯 합니다.
결과 화면
실행 결과는 다음과 같은 모양을 하고 있습니다.
ref