我有一個 PowerShell 腳本,我想在其中創建一個后臺執行緒并與我的主執行緒動態交換資料。這個想法是使用資訊流,因為它可以輕松處理各種物件。
通常我通過將 PowerShell-Object 賦予自身來做到這一點,如下所示:
$Code =
{
Param($Me)
#Here I can use $Me.Streams.Information to exchange data any time,
#for example to feed my thread with more work to do on the fly
$ResultData = [System.Object[]]::new(0)
$WorkCounter = 0
$Finished = $false
while (-not $Finished)
{
while ($Me.Streams.Information.Count -eq $WorkCounter)
{
#Wait for data to be added to the information stream
Sleep -MilliSeconds 10
}
$InputData = $Me.Streams.Information[-1].MessageData
if ($InputData -eq "FINISHED")
{
$Finished = $true
}
else
{
<# Do some stuff with the $InputData #>
$ResultData = $ProgressedInputData
}
$WorkCounter
}
Write-Information $ResultData
}
$PS = [PowerShell]::Create()
$PS.AddScript($Code) | Out-Null
$PS.AddArgument($PS) | Out-Null #Hand the PS to itself to make the streams accessible inside the thread
$Handle = $PS.BeginInvoke() | Out-Null
for ($i = 0; $i -lt 10; $i )
{
$PS.Streams.Information.Add([System.Management.Automation.InformationRecord]::new($i, ""))
#I just gave my background thread some stuff to do without the need to instantiate a new one again
#Now this thread can do some work too...
}
$PS.Streams.Information.Add([System.Management.Automation.InformationRecord]::new("FINISHED", ""))
$Handle.AsyncWaitHandle.WaitOne() #Wait for my background thread to finish all its work
$SomeReturnValue = $PS.Streams.Information[-1].MessageData
我的實際問題是:是否可以訪問當前的 PowerShell 實體而無需像使用 $PS.AddArgument($PS) 那樣將其移交?
uj5u.com熱心網友回復:
您不需要在PowerShell.Streams這里濫用雙向通信 - PowerShell 已經有一個用于在兩個運行空間之間橋接會話狀態的工具,即所謂的會話狀態代理!
讓我們從稍微重寫你的$code塊開始:
$code = {
while($config['Enabled']){
$inputData = $null
if($queue.TryDequeue([ref]$inputData)) {
#process input data
Write-Information $inputData
}
else {
Start-Sleep -Milliseconds 50
}
}
}
請注意,我使用了兩個變數$config和$queue,盡管我實際上沒有引數化或以其他方式定義它們,但我們仍然使用資訊流來傳達輸出(您也可以使用標準輸出)。
現在我們只需要創建兩個可以系結到這些變數的物件。為了執行緒安全,我們將使用:
- A
ConcurrentQueue[psobject]為輸入資料 [hashtable]配置資料的執行緒同步
# Create PowerShell instance like before
$PS = [powershell]::Create()
# Create the thread-safe collections we'll be using to communicate
$queue = [System.Collections.Concurrent.ConcurrentQueue[psobject]]::new()
$config = [hashtable]::Synchronized(@{
Enabled = $true
})
# Now make those variable references available in the runspace where the backgroun code will be running
$ps.Runspace.SessionStateProxy.PSVariable.Set('queue', $queue)
$ps.Runspace.SessionStateProxy.PSVariable.Set('config', $config)
借助用于交換輸入和配置資料的工具,您現在可以呼叫后臺作業并根據通過提供的輸入觀察它的行為$queue(我強烈建議將以下陳述句輸入到互動式提示中,稍微嘗試一下) :
# Invoke background code
$asyncHandle = $PS.BeginInvoke()
# Try adding some data to the queue
$queue.TryAdd([pscustomobject]@{ Property = "Value 123"})
# Wait for a bit
Start-Sleep -Milliseconds 100
# Observe that the queue has been emptied by the background code
Write-Host "Queue is empty: $($queue.IsEmpty)"
# Observe that the background code actually processed (and output) the data
$ps.Streams.Information
uj5u.com熱心網友回復:
讓我根據模塊的cmdlet提供Mathias R. Jessen 的有用答案的替代方案,該模塊隨PowerShell (Core) v6 一起提供,并且在Windows PowerShell中可以按需安裝(例如,)ThreadJobStart-ThreadJobInstall-Module ThreadJob -Scope CurrentUser
顧名思義,它提供了基于執行緒的后臺操作,作為由創建的基于子行程的后臺作業的更快和更輕量級的替代方案Start-Job(請參閱此答案以獲得并列)。
因此,它是通過PowerShell SDK管理多個執行緒(運行空間)的更友好、更高級別的替代方案,允許您使用通常的作業管理 cmdlet 與后臺執行緒進行互動。
一個簡單的例子:
# Create a synchronized (thread-safe) queue.
$threadSafeQueue = [System.Collections.Concurrent.ConcurrentQueue[string]]::new()
# Start a thread job that keeps processing elements in the queue
# indefinitely, sleeping a little between checks for new elements.
# A special element value is used to signal that processing should end.
$jb = Start-ThreadJob {
$q = $using:threadSafeQueue # get a reference to the thread-safe queue.
$element = $null # variable to receive queue elements
while ($true) {
# Process all elements currently in the queue.
while ($q.TryDequeue([ref] $element)) {
# Check for the signal to quit, by convention a single NUL char. here.
if ("`0" -eq $element) { 'Quitting...'; return }
# Process the element at hand.
# In this example, echo the dequeued element enclosed in "[...]"
'[{0}]' -f $element
}
# Queue is (now) empty, sleep a little before checking for new elements.
Start-Sleep -MilliSeconds 100
}
}
# Populate the queue with the numbers from 1 to 10.
1..10 | ForEach-Object {
$threadSafeQueue.Enqueue($_) # This triggers activity in the background thread.
# Retrieve available output from the thread job.
$jb | Receive-Job
}
# Send the quit signal, retrieve remaining output and delete the job.
$threadSafeQueue.Enqueue("`0")
$jb | Receive-Job -Wait -AutoRemoveJob
輸出:
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
Quitting...
也可以看看:
- PowerShell (Core) v7
Foreach-Object -Parallel功能,類似地使用執行緒來并行處理管道輸入。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/424563.html
上一篇:java中實體變數的使用
