如何阻止无限线程

时间:2017-11-22 10:59:39

标签: multithreading powershell

我有一个powershell脚本,它创建了两个带有无限循环的线程(仅用于示例)。

当我按Ctrl + Z时,它试图杀死所有线程 但它没有成功:
enter image description here

为了创建我使用BeginInvoke的帖子,当我想要停止它时,我使用EndInvoke
问题是,根据MicrosoftEndInvoke将等待线程的BeginInvoke完成,但线程将无法完成,因为它具有无限循环:

  

等待挂起的异步BeginInvoke完成。

当用户按Ctrl + Z时,如何杀死所有线程?

我读了一个类似的问题:
How to kill threads started in PowerShell script on stop

但它正在使用也使用EndInvoke的管道。

这是PowerShell中的一个小脚本,它演示了我上面写的内容:

function Invoke-Main(){
    [scriptblock]$code = {
            while($true){
                Start-Sleep 2
                Write-Host "TID: "$([System.Threading.Thread]::CurrentThread.ManagedThreadId)
            }
    }

#requires -Version 2
function Test-KeyPress
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.Windows.Forms.Keys[]]
        $Keys
    )

    # use the User32 API to define a keypress datatype
    $Signature = @'
    [DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)] 
    public static extern short GetAsyncKeyState(int virtualKeyCode); 
'@
    $API = Add-Type -MemberDefinition $Signature -Name 'Keypress' -Namespace Keytest -PassThru

    # test if each key in the collection is pressed
    $Result = foreach ($Key in $Keys)
    {
        [bool]($API::GetAsyncKeyState($Key) -eq -32767)
    }

    # if all are pressed, return true, if any are not pressed, return false
    $Result -notcontains $false
}

    function Kill-AllThreadsOnKeyBreak($Jobs){
        Start-Sleep -Seconds 1
        $ctrlZPressed = $false
        if($host.name -eq 'ConsoleHost'){
            if ([console]::KeyAvailable)
            {
                $key = [system.console]::readkey($true)
                if (($key.modifiers -band [consolemodifiers]"control") -and ($key.key -eq "Z"))
                {
                    $ctrlZPressed = $true
                }
            }
        }
        else{
            $ctrlZPressed = Test-KeyPress -Keys ([System.Windows.Forms.Keys]::ControlKey),([System.Windows.Forms.Keys]::Z)
        }

        if ($ctrlZPressed){
            "Terminating..."
            ForEach ($Job in $Jobs){
                Write-Host "[START] EndInvoke"
                $Job.Thread.EndInvoke($Job.Handle)
                Write-Host "[END] EndInvoke"
                $Job.Thread.Dispose()
                $Job.Thread = $Null
                $Job.Handle = $Null
                $ResultTimer = Get-Date
            }
            Write-Verbose "Termination completed"

        }
    }

    $SleepTimer = 200
    $MaxResultTime = 120
    $MaxThreads = 3

    $ISS = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
    $RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads, $ISS, $Host)
    $RunspacePool.Open()

    $Jobs = @()

    [array]$computerList = @("Computer1", "Computer2")
    foreach($computera in $computerList){
        $PowershellThread = [powershell]::Create().AddScript($code)
        $PowershellThread.RunspacePool = $RunspacePool
        $Handle = $PowershellThread.BeginInvoke()
        $Job = "" | Select-Object Handle, Thread, object
        $Job.Handle = $Handle
        $Job.Thread = $PowershellThread
        $Job.Object = $computer
        $Jobs += $Job
    }

    While (@($Jobs | Where-Object {$_.Handle -ne $Null}).count -gt 0)  {
        Kill-AllThreadsOnKeyBreak $Jobs
        Start-Sleep -Milliseconds $SleepTimer
    }

    $RunspacePool.Close() | Out-Null
    $RunspacePool.Dispose() | Out-Null
}

Invoke-Main

1 个答案:

答案 0 :(得分:0)

我重新检查了方法Stop,将$Job.Thread.EndInvoke()替换为$Job.Thread.Stop()后效果正常。

可以找到对此命令的引用here 根据微软的说法:

  

同步停止当前运行的命令。