Winforms 单独运行空间中的管道列表视图项
我有一个用PowerShell编写的表单(因为它使用PS在数百台服务器上运行命令),但这一点让我困惑了一段时间:Winforms 单独运行空间中的管道列表视图项,winforms,listview,powershell,runspace,powershell-5.0,Winforms,Listview,Powershell,Runspace,Powershell 5.0,我有一个用PowerShell编写的表单(因为它使用PS在数百台服务器上运行命令),但这一点让我困惑了一段时间: $listview.Invoke([System.Action[String]]{ Param ( [String]$data ) write-warning "1" $LVitem = ($LVresults.Items | Where { $_.Name -eq "Test Account" }) write-war
$listview.Invoke([System.Action[String]]{
Param
(
[String]$data
)
write-warning "1"
$LVitem = ($LVresults.Items | Where { $_.Name -eq "Test Account" })
write-warning "2"
$LVitem.Tag.Output += $data + "`n"
}, "Testing")
它位于一个单独的运行空间中,该运行空间向特定服务器运行Invoke命令,并将输出通过管道传输到此代码块
现在,如果我在主运行空间(创建表单的地方)中运行Where语句,它就可以正常工作了。但在单独的运行空间中,它会锁定窗体。显示警告1,不显示警告2
到Foreach语句的管道也有同样的问题,但我可以使用:
Foreach ($item in $listview.Items) {
if ($item.Name -eq "Test Account") { $LVitem = $item }
}
有人能解释一下吗?我没有对ListView或其项做任何花哨的处理,只是ListView似乎不喜欢将其项通过管道传输到另一个运行空间。这就是PowerShell事件系统的问题。当事件处理程序使用“等待完成”选项生成事件时,它会死锁
Register-EngineEvent -SourceIdentifier MyEvent1 -Action {Write-Host 'Does not get called.'}
Register-EngineEvent -SourceIdentifier MyEvent2 -Action {$ExecutionContext.Events.GenerateEvent('MyEvent1',$null,$null,$null,$true,$true)}
New-Event -SourceIdentifier MyEvent2
那么,您的设置与事件有什么关系
脚本块文本在创建时记住当前会话状态(包括运行空间)。如果在脚本块调用时,当前线程([Runspace]::DefaultRunspace
)的当前运行空间
)不同或正忙于处理不同线程中的某些内容,则通过使用“等待完成”选项生成原始运行空间的事件来调用脚本块
Register-EngineEvent -SourceIdentifier MyEvent1 -Action {Write-Host 'Does not get called.'}
Register-EngineEvent -SourceIdentifier MyEvent2 -Action {$ExecutionContext.Events.GenerateEvent('MyEvent1',$null,$null,$null,$true,$true)}
New-Event -SourceIdentifier MyEvent2
另一件有趣的事情是:如果targetRunspace
在250毫秒内没有开始处理事件,那么PowerShell将在等待事件完成的线程中开始事件处理
在代码中有两个嵌套的脚本块调用:
脚本块作为委托传递给$listview.Invoke
方法
脚本块传递到Where Object
cmdlet
如我所见,您的代码有三种可能的结果:
脚本块的原始运行空间是免费的,所以脚本块在不同的线程(不是UI线程)中执行
脚本块的原始运行空间<代码>很忙,所以脚本块在当前线程中执行,并在嵌套脚本块调用时死锁
$PowerShell=[PowerShell]::Create()
$PowerShell.Runspace=[RunspaceFactory]::CreateRunspace($Host)
$PowerShell.Runspace.Open()
$Stopwatch=[Diagnostics.Stopwatch]::new()
$PowerShell.Runspace.SessionStateProxy.PSVariable.Set('Stopwatch',$Stopwatch)
$ScriptBlock=$PowerShell.AddScript{{
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
{Write-Host Deadlock}.Invoke()
}}.Invoke()
$PowerShell.Commands.Clear()
& {
$AsyncResult=$PowerShell.AddScript{Start-Sleep 10}.BeginInvoke()
$Stopwatch.Start()
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
$ScriptBlock.Invoke()
}
脚本块的原始运行空间最初很忙,但在嵌套脚本块调用之前可以释放,所以脚本块在当前线程中执行,不会死锁
$PowerShell=[PowerShell]::Create()
$PowerShell.Runspace=[RunspaceFactory]::CreateRunspace($Host)
$PowerShell.Runspace.Open()
$Stopwatch=[Diagnostics.Stopwatch]::new()
$PowerShell.Runspace.SessionStateProxy.PSVariable.Set('Stopwatch',$Stopwatch)
$ScriptBlock=$PowerShell.AddScript{{
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
Start-Sleep 10
{Write-Host OK}.Invoke()
}}.Invoke()
$PowerShell.Commands.Clear()
& {
$AsyncResult=$PowerShell.AddScript{Start-Sleep 5}.BeginInvoke()
$Stopwatch.Start()
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
$ScriptBlock.Invoke()
}
要解决此问题,需要将脚本块与其原始运行空间分离。您可以通过使用[ScriptBlock]::Create
方法从字符串创建新的脚本块来实现这一点
$PowerShell=[PowerShell]::Create()
$PowerShell.Runspace=[RunspaceFactory]::CreateRunspace($Host)
$PowerShell.Runspace.Open()
$Stopwatch=[Diagnostics.Stopwatch]::new()
$PowerShell.Runspace.SessionStateProxy.PSVariable.Set('Stopwatch',$Stopwatch)
$ScriptBlock=$PowerShell.AddScript{[ScriptBlock]::Create{
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
{Write-Host OK}.Invoke()
}}.Invoke()
$PowerShell.Commands.Clear()
& {
$AsyncResult=$PowerShell.AddScript{Start-Sleep 10}.BeginInvoke()
$Stopwatch.Start()
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
$ScriptBlock.Invoke()
}
这就是PowerShell事件系统的问题。当事件处理程序使用“等待完成”选项生成事件时,它会死锁
Register-EngineEvent -SourceIdentifier MyEvent1 -Action {Write-Host 'Does not get called.'}
Register-EngineEvent -SourceIdentifier MyEvent2 -Action {$ExecutionContext.Events.GenerateEvent('MyEvent1',$null,$null,$null,$true,$true)}
New-Event -SourceIdentifier MyEvent2
那么,您的设置与事件有什么关系
脚本块文本在创建时记住当前会话状态(包括运行空间)。如果在脚本块调用时,当前线程([Runspace]::DefaultRunspace
)的当前运行空间
)不同或正忙于处理不同线程中的某些内容,则通过使用“等待完成”选项生成原始运行空间的事件来调用脚本块
Register-EngineEvent -SourceIdentifier MyEvent1 -Action {Write-Host 'Does not get called.'}
Register-EngineEvent -SourceIdentifier MyEvent2 -Action {$ExecutionContext.Events.GenerateEvent('MyEvent1',$null,$null,$null,$true,$true)}
New-Event -SourceIdentifier MyEvent2
另一件有趣的事情是:如果targetRunspace
在250毫秒内没有开始处理事件,那么PowerShell将在等待事件完成的线程中开始事件处理
在代码中有两个嵌套的脚本块调用:
脚本块作为委托传递给$listview.Invoke
方法
脚本块传递到Where Object
cmdlet
如我所见,您的代码有三种可能的结果:
脚本块的原始运行空间是免费的,所以脚本块在不同的线程(不是UI线程)中执行
脚本块的原始运行空间<代码>很忙,所以脚本块在当前线程中执行,并在嵌套脚本块调用时死锁
$PowerShell=[PowerShell]::Create()
$PowerShell.Runspace=[RunspaceFactory]::CreateRunspace($Host)
$PowerShell.Runspace.Open()
$Stopwatch=[Diagnostics.Stopwatch]::new()
$PowerShell.Runspace.SessionStateProxy.PSVariable.Set('Stopwatch',$Stopwatch)
$ScriptBlock=$PowerShell.AddScript{{
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
{Write-Host Deadlock}.Invoke()
}}.Invoke()
$PowerShell.Commands.Clear()
& {
$AsyncResult=$PowerShell.AddScript{Start-Sleep 10}.BeginInvoke()
$Stopwatch.Start()
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
$ScriptBlock.Invoke()
}
脚本块的原始运行空间最初很忙,但在嵌套脚本块调用之前可以释放,所以脚本块在当前线程中执行,不会死锁
$PowerShell=[PowerShell]::Create()
$PowerShell.Runspace=[RunspaceFactory]::CreateRunspace($Host)
$PowerShell.Runspace.Open()
$Stopwatch=[Diagnostics.Stopwatch]::new()
$PowerShell.Runspace.SessionStateProxy.PSVariable.Set('Stopwatch',$Stopwatch)
$ScriptBlock=$PowerShell.AddScript{{
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
Start-Sleep 10
{Write-Host OK}.Invoke()
}}.Invoke()
$PowerShell.Commands.Clear()
& {
$AsyncResult=$PowerShell.AddScript{Start-Sleep 5}.BeginInvoke()
$Stopwatch.Start()
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
$ScriptBlock.Invoke()
}
要解决此问题,需要将脚本块与其原始运行空间分离。您可以通过使用[ScriptBlock]::Create
方法从字符串创建新的脚本块来实现这一点
$PowerShell=[PowerShell]::Create()
$PowerShell.Runspace=[RunspaceFactory]::CreateRunspace($Host)
$PowerShell.Runspace.Open()
$Stopwatch=[Diagnostics.Stopwatch]::new()
$PowerShell.Runspace.SessionStateProxy.PSVariable.Set('Stopwatch',$Stopwatch)
$ScriptBlock=$PowerShell.AddScript{[ScriptBlock]::Create{
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
{Write-Host OK}.Invoke()
}}.Invoke()
$PowerShell.Commands.Clear()
& {
$AsyncResult=$PowerShell.AddScript{Start-Sleep 10}.BeginInvoke()
$Stopwatch.Start()
Write-Host "$($Stopwatch.ElapsedMilliseconds): $([Threading.Thread]::CurrentThread.ManagedThreadId)"
$ScriptBlock.Invoke()
}
如果用{}.Invoke()替换$LVitem=($LVresults.Items | Where{${.Name-eq“Test Account”})
,它会锁定表单吗。实际上是这样的。如果您将$LVitem=($LVresults.Items | Where{$\uu.Name-eq“Test Account”})
替换为{}.Invoke()
?,它会锁定表单吗。事实上是的,我没有足够的票数来回答这个问题。谢谢我没有足够的票数给出这个答案。谢谢