자바스크립트를 활성화 해주세요

p076 Powershell Multi Runspace 로 멀티태스킹 하기

 ·  ☕ 3 min read

멀티 태스킹으로 몇가지 테크닉을 작성한 적이 있습니다만,

그 중에서도 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부분과 그에 넘기는 파라메터만 정의해서 사용하시면 될 듯 합니다.

결과 화면

실행 결과는 다음과 같은 모양을 하고 있습니다.

p075_kokoronarazumo

ref

공유하기

tkim
글쓴이
tkim
Software Engineer