อัปเดต - ในขณะที่คำตอบนี้อธิบายถึงกระบวนการและกลไกของพื้นที่ทำงาน PowerShell และวิธีที่พวกเขาสามารถช่วยคุณทำงานแบบมัลติเธรดที่ไม่ต่อเนื่องได้ แต่ PowerShell ผู้คลั่งไคล้ PowerShell 'Cookie Monster' Fได้ผ่านไปแล้ว เรียกว่า - มันเป็นสิ่งที่ฉันอธิบายด้านล่างและเขาได้ขยายด้วยสวิตช์ตัวเลือกสำหรับการบันทึกและสถานะเซสชันที่เตรียมไว้รวมถึงโมดูลที่นำเข้าสิ่งที่ยอดเยี่ยมจริง ๆ - ฉันขอแนะนำให้คุณตรวจสอบก่อนสร้างโซลูชันเงา!Invoke-Parallel
ด้วยการประมวลผล Parallel Runspace:
การลดเวลารอที่หลีกเลี่ยงไม่ได้
ในกรณีเฉพาะดั้งเดิมไฟล์เรียกทำงานที่เรียกใช้งานมี/nowait
ตัวเลือกที่ป้องกันการบล็อกเธรดที่เรียกใช้ในขณะที่งาน (ในกรณีนี้เวลาซิงโครไนซ์อีกครั้ง) จะเสร็จสิ้นด้วยตัวเอง
สิ่งนี้ช่วยลดเวลาการดำเนินการโดยรวมจากมุมมองของผู้ออกตราสารได้มาก แต่การเชื่อมต่อกับแต่ละเครื่องยังคงดำเนินการตามลำดับ การเชื่อมต่อกับไคลเอนต์หลายพันเครื่องตามลำดับอาจใช้เวลานานขึ้นอยู่กับจำนวนของเครื่องที่มีสาเหตุหนึ่งหรือไม่สามารถเข้าถึงได้อีกเนื่องจากการหมดเวลาของการรอคอยสะสม
หากต้องการหลีกเลี่ยงการต่อคิวการเชื่อมต่อที่ตามมาทั้งหมดในกรณีที่มีการหมดเวลาติดต่อกันเพียงครั้งเดียวหรือสองสามครั้งเราสามารถจัดส่งงานของการเชื่อมต่อและเรียกใช้คำสั่งเพื่อแยก PowerShell Runspaces ออกจากกัน
Runspace คืออะไร
Runspaceเป็นภาชนะเสมือนในที่รันรหัส PowerShell ของคุณและเป็นตัวแทน / ถือสิ่งแวดล้อมจากมุมมองของคำสั่ง PowerShell / คำสั่ง
ในแง่กว้าง 1 Runspace = 1 เธรดของการประมวลผลดังนั้นสิ่งที่เราต้อง "มัลติเธรด" สคริปต์ PowerShell ของเราคือชุดของ Runspaces ที่สามารถเปิดใช้งานแบบขนาน
เช่นเดียวกับปัญหาดั้งเดิมงานของคำสั่งที่ใช้เรียกใช้งานหลายพื้นที่ทำงานสามารถแบ่งย่อยได้เป็น:
- สร้าง RunspacePool
- การกำหนดสคริปต์ PowerShell หรือรหัสที่สามารถใช้งานได้เทียบเท่ากับ RunspacePool
- เรียกใช้โค้ดแบบอะซิงโครนัส (เช่นไม่ต้องรอให้โค้ดส่งคืน)
เทมเพลต RunspacePool
PowerShell มีตัวเร่งความเร็วชนิดที่เรียก[RunspaceFactory]
ว่าจะช่วยเราในการสร้างส่วนประกอบของรันสเปซ - มาทำให้ใช้งานได้
1. สร้าง RunspacePool และสร้างOpen()
:
$RunspacePool = [runspacefactory]::CreateRunspacePool(1,8)
$RunspacePool.Open()
ทั้งสองมีปากเสียงส่งผ่านไปCreateRunspacePool()
, 1
และ8
เป็นต่ำสุดและสูงสุดจำนวน runspaces ได้รับอนุญาตให้ดำเนินการในเวลาใดก็ตามให้เรามีประสิทธิภาพสูงสุดระดับการขนาน 8
2. สร้างอินสแตนซ์ของ PowerShell แนบรหัสที่สามารถเรียกใช้งานได้และกำหนดให้กับ RunspacePool ของเรา:
อินสแตนซ์ของ PowerShell ไม่เหมือนกับpowershell.exe
กระบวนการ (ซึ่งเป็นแอปพลิเคชัน Host จริงๆ) แต่เป็นวัตถุรันไทม์ภายในที่แสดงรหัส PowerShell เพื่อเรียกใช้งาน เราสามารถใช้[powershell]
ตัวเร่งความเร็วประเภทเพื่อสร้างอินสแตนซ์ PowerShell ใหม่ภายใน PowerShell:
$Code = {
param($Credentials,$ComputerName)
$session = New-PSSession -ComputerName $ComputerName -Credential $Credentials
Invoke-Command -Session $session -ScriptBlock {w32tm /resync /nowait /rediscover}
}
$PSinstance = [powershell]::Create().AddScript($Code).AddArgument($creds).AddArgument("computer1.domain.tld")
$PSinstance.RunspacePool = $RunspacePool
3. เรียกใช้อินสแตนซ์ PowerShell แบบอะซิงโครนัสโดยใช้ APM:
การใช้สิ่งที่เป็นที่รู้จักกันในคำศัพท์การพัฒนา. NET เป็นAsynchronous Programming Modelเราสามารถแบ่งการร้องขอของคำสั่งเป็นBegin
วิธีการให้ "ไฟเขียว" เพื่อรันโค้ดและEnd
วิธีการรวบรวมผลลัพธ์ เนื่องจากเราในกรณีนี้ไม่สนใจข้อเสนอแนะใด ๆ (เราไม่รอผลลัพธ์จากw32tm
anyways) เราสามารถทำการครบกำหนดได้เพียงแค่เรียกวิธีแรก
$PSinstance.BeginInvoke()
สรุปใน RunspacePool
การใช้เทคนิคด้านบนเราสามารถล้อมรอบการวนซ้ำของการสร้างการเชื่อมต่อใหม่และเรียกใช้คำสั่งระยะไกลในการไหลของการประมวลผลแบบขนาน:
$ComputerNames = Get-ADComputer -filter * -Properties dnsHostName |select -Expand dnsHostName
$Code = {
param($Credentials,$ComputerName)
$session = New-PSSession -ComputerName $ComputerName -Credential $Credentials
Invoke-Command -Session $session -ScriptBlock {w32tm /resync /nowait /rediscover}
}
$creds = Get-Credential domain\user
$rsPool = [runspacefactory]::CreateRunspacePool(1,8)
$rsPool.Open()
foreach($ComputerName in $ComputerNames)
{
$PSinstance = [powershell]::Create().AddScript($Code).AddArgument($creds).AddArgument($ComputerName)
$PSinstance.RunspacePool = $rsPool
$PSinstance.BeginInvoke()
}
สมมติว่าซีพียูมีความสามารถในการดำเนินการทั้ง 8 รันสเปซในครั้งเดียวเราจะสามารถเห็นได้ว่าเวลาในการประมวลผลลดลงอย่างมาก แต่มีค่าใช้จ่ายในการอ่านสคริปต์เนื่องจากวิธีการ "ขั้นสูง"
การกำหนดระดับสูงสุดของความเท่าเทียมกัน:
เราสามารถสร้าง RunspacePool ที่อนุญาตให้เรียกใช้งาน 100 runspaces ในเวลาเดียวกันได้อย่างง่ายดาย:
[runspacefactory]::CreateRunspacePool(1,100)
แต่ในตอนท้ายของวันมันทั้งหมดลงมาถึงจำนวนหน่วยปฏิบัติการซีพียูของเราสามารถจัดการได้ กล่าวอีกนัยหนึ่งตราบใดที่โค้ดของคุณกำลังทำงานมันไม่เหมาะสมที่จะอนุญาตให้รันสเปซมากกว่าที่คุณมีตัวประมวลผลเชิงตรรกะเพื่อส่งการเรียกใช้โค้ดไป
ขอบคุณ WMI เกณฑ์นี้ค่อนข้างง่ายในการพิจารณา:
$NumberOfLogicalProcessor = (Get-WmiObject Win32_Processor).NumberOfLogicalProcessors
[runspacefactory]::CreateRunspacePool(1,$NumberOfLogicalProcessors)
หากในทางกลับกันโค้ดที่คุณกำลังเรียกใช้งานเองนั้นใช้เวลาในการรอนานมากเนื่องจากปัจจัยภายนอกเช่นเวลาแฝงของเครือข่ายคุณยังคงได้รับประโยชน์จากการรันรันไทม์พื้นที่มากกว่าที่คุณมีตัวประมวลผลเชิงตรรกะดังนั้นคุณอาจต้องการทดสอบ ของช่วงความเร็วสูงสุดที่เป็นไปได้ในการหาจุดคุ้มทุน :
foreach($n in ($NumberOfLogicalProcessors..($NumberOfLogicalProcessors*3)))
{
Write-Host "$n: " -NoNewLine
(Measure-Command {
$Computers = Get-ADComputer -filter * -Properties dnsHostName |select -Expand dnsHostName -First 100
...
[runspacefactory]::CreateRunspacePool(1,$n)
...
}).TotalSeconds
}