我有一个powershell脚本,它创建了两个带有无限循环的线程(仅用于示例)。
当我按Ctrl + Z
时,它试图杀死所有线程
但它没有成功:
为了创建我使用BeginInvoke
的帖子,当我想要停止它时,我使用EndInvoke
。
问题是,根据Microsoft,EndInvoke
将等待线程的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
答案 0 :(得分:0)
我重新检查了方法Stop
,将$Job.Thread.EndInvoke()
替换为$Job.Thread.Stop()
后效果正常。
可以找到对此命令的引用here 根据微软的说法:
同步停止当前运行的命令。