PowerShell: In einen Job eine Variable auf Script-Ebene ändern
Hi,
gegeben sei in PowerShell auf Script-Ebene ein Array mit tausenden Objekten.
Jetzt sollen zwei Jobs diese Objekte parallel bearbeiten. Der eine Job die Prop1, der andere die Prop2.
z.B.
Nach dem Ende der beiden Jobs soll auf Script-Ebene in den Elementen von $A die Properties Prop1 = "Eins" und Prop2 = "Zwei" sein.
Ich habe es mit den Scopes "script:" und "global:" versucht, aber beides funktioniert nicht.
Wie muss ich das anstellen, damit das so funktioniert, wie ich mir das vorstelle?
E.
gegeben sei in PowerShell auf Script-Ebene ein Array mit tausenden Objekten.
$A = @()
$A += New-Object PSObject -Property @{Prop1=$null; Prop2=$null}
$A += New-Object PSObject -Property @{Prop1=$null; Prop2=$null}
$A += New-Object PSObject -Property @{Prop1=$null; Prop2=$null}
$A += New-Object PSObject -Property @{Prop1=$null; Prop2=$null}
....
z.B.
$Job1 = Start-Job -ScriptBlock {$A | %{$_.Prop1 = "Eins"}}
$Job2 = Start-Job -ScriptBlock {$A | %{$_.Prop2 = "Zwei"}}
Ich habe es mit den Scopes "script:" und "global:" versucht, aber beides funktioniert nicht.
Wie muss ich das anstellen, damit das so funktioniert, wie ich mir das vorstelle?
E.
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 7947992246
Url: https://administrator.de/contentid/7947992246
Ausgedruckt am: 18.11.2024 um 13:11 Uhr
3 Kommentare
Neuester Kommentar
Moin.
Jobs laufen in separaten Threads darin kannst du keine Variablen der höheren Ebene verändern das ergäbe einen Illegal Cross-Thread-Call. Was du dort machen kannst ist ein Ergebnis zurückzugeben und mit Receive-Job das Ergebnis abzufragen und im Parent-Thread wieder einer Variablen zuweisen.
Aber für tausende Objekte sind "Powershell-Jobs" eh nicht so gut geeignet, da nimmt man besser gleich Runspaces/Pools die sind wesentlich schneller vor allem wenn man diese dann auch noch mit einem Runspace-Pool kombiniert. Dort kannst du auch mit Synchronized Hashtables arbeiten.
Hier mal ein Beispiel wie man aus einem separaten Thread Eigenschaften des Parent-Threads mittels Synchronized Hashtable ändern kann:
Gruß siddius
Jobs laufen in separaten Threads darin kannst du keine Variablen der höheren Ebene verändern das ergäbe einen Illegal Cross-Thread-Call. Was du dort machen kannst ist ein Ergebnis zurückzugeben und mit Receive-Job das Ergebnis abzufragen und im Parent-Thread wieder einer Variablen zuweisen.
Aber für tausende Objekte sind "Powershell-Jobs" eh nicht so gut geeignet, da nimmt man besser gleich Runspaces/Pools die sind wesentlich schneller vor allem wenn man diese dann auch noch mit einem Runspace-Pool kombiniert. Dort kannst du auch mit Synchronized Hashtables arbeiten.
Hier mal ein Beispiel wie man aus einem separaten Thread Eigenschaften des Parent-Threads mittels Synchronized Hashtable ändern kann:
# create form
$form = [System.Windows.Forms.Form]@{
Text = "Windows Forms Multi-Threading"
Size = '450,150'
}
# create label
$label = [System.Windows.Forms.Label]@{
Name = 'label1'
Location = '10,10'
Text = "Click me to start background task"
Dock = 'Fill'
TextAlign = 'MiddleCenter'
Font = [System.Drawing.Font]::new('Calibri',15)
}
# add click event to the label
$label.add_Click({
if ($cmd.InvocationStateInfo.State -ne 'Running'){
# start background script
$result = $cmd.BeginInvoke()
}
})
# event when closing form
$form.add_Closed({
# end backgound script if it's still running
if ($cmd.InvocationStateInfo.State -ne 'Completed'){
$cmd.Stop()
}
$cmd.Dispose()
})
# create synchronized hashtable for bidirectional communication across threads
$ht = [hashtable]::Synchronized(@{})
# create runspace
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open()
# populate the synchronized Hashtable Variable to the runspace (make it available to the script)
$newRunspace.SessionStateProxy.SetVariable("ht",$ht)
# add the whole form object to the hashtable, so we can access the whole form including it's nested controls
$ht.Form = $form
# create new powershell process
$cmd = [powershell]::Create()
# define the script to execute in the process
[void]$cmd.AddScript({
# everything here is executed in another thread
# example countdown
3..1 | %{
# use the created object in the hashtable to change text in the label of the form
$ht.Form.Controls['label1'].Text = "Background-Task is running, wait $_ seconds ..."
sleep 1
}
$ht.Form.Controls['label1'].Text = "Finished"
sleep 1
$ht.Form.Controls['label1'].Text = "Click me to start background task"
})
# assign the runspace to the process
$cmd.Runspace = $newRunspace
# add controls to the form
$form.Controls.AddRange(@($label))
# show form
[void]$form.ShowDialog()
Moin,
nimm statt dem normalen Array eine Synchronisierte ArrayList und weise diese einem Runspace als Variable zu.
tio.run
Gruß Thomas
nimm statt dem normalen Array eine Synchronisierte ArrayList und weise diese einem Runspace als Variable zu.
$A = [System.Collections.ArrayList]::Synchronized(@())
$A += New-Object PSObject -Property @{Prop1=$null; Prop2=$null}
$A += New-Object PSObject -Property @{Prop1=$null; Prop2=$null}
$A += New-Object PSObject -Property @{Prop1=$null; Prop2=$null}
$A += New-Object PSObject -Property @{Prop1=$null; Prop2=$null}
$Job1 = [powershell]::Create().AddScript({$A | %{$_.Prop1 = "Eins"}})
$Job1.Runspace.SessionStateProxy.SetVariable("A",$A)
$Job2 = [powershell]::Create().AddScript({$A | %{$_.Prop2 = "Zwei"}})
$Job2.Runspace.SessionStateProxy.SetVariable("A",$A)
$Handle1 = $Job1.BeginInvoke()
$Handle2 = $Job2.BeginInvoke()
while (!$Handle1.IsCompleted -or !$Handle2.IsCompleted) {sleep 1}
$A
Gruß Thomas