Winforms 单独运行空间中的管道列表视图项

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

我有一个用PowerShell编写的表单(因为它使用PS在数百台服务器上运行命令),但这一点让我困惑了一段时间:

$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
另一件有趣的事情是:如果target
Runspace
在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
    
    另一件有趣的事情是:如果target
    Runspace
    在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()
    ?,它会锁定表单吗。事实上是的,我没有足够的票数来回答这个问题。谢谢我没有足够的票数给出这个答案。谢谢