title: How to stop infinite thread
body: <p>I have a powershell script that creates two threads with infinite loop (just for the example).  </p>
  
  <p>When I am pressing <code>Ctrl + Z</code> it is trying to kill all the threads.<br>
  But it doesn't succeed:<br>
  <a href="https://i.stack.imgur.com/83Oja.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/83Oja.png" alt="enter image description here"></a></p>
  
  <p>In order to create the thread I used <code>BeginInvoke</code> and when I wanted to stop it I used <code>EndInvoke</code>.<br>
  The problem is that <code>EndInvoke</code>, according to <a href="https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.powershell.endinvoke?view=powershellsdk-1.1.0" rel="nofollow noreferrer">Microsoft</a>, will wait for the thread's <code>BeginInvoke</code> to complete but the thread won't complete because it has infinite loop:   </p>
  
  <blockquote>
    <p>Waits for the pending asynchronous BeginInvoke to complete.</p>
  </blockquote>
  
  <p>How can I kill all the threads when the user press <code>Ctrl + Z</code> ?  </p>
  
  <p>I read a similar question:<br>
  <a href="https://stackoverflow.com/questions/28850652/how-to-kill-threads-started-in-powershell-script-on-stop">How to kill threads started in PowerShell script on stop</a>  </p>
  
  <p>But it is working with pipes that also use <code>EndInvoke</code>.  </p>
  
  <p>This is a small script in PowerShell that demonstarte what I wrote above:  </p>
  
  <pre><code>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
  </code></pre>