'Access current PowerShell instance

I have a PowerShell script where I want to create a background thread and dynamically exchange data with my primary thread. The idea was to use the information stream since it can handle all kind of objects easily.

Usually I do so by giving the PowerShell-Object to itself like the following:

$Code =
{
    Param($Me)
    #Here I can use $Me.Streams.Information to exchange data any time,
    #for example to feed my thread with more work to do on the fly

    $ResultData = [System.Object[]]::new(0)
    $WorkCounter = 0
    $Finished = $false
    while (-not $Finished)
    {
        while ($Me.Streams.Information.Count -eq $WorkCounter)
        {
            #Wait for data to be added to the information stream
            Sleep -MilliSeconds 10
        }

        $InputData = $Me.Streams.Information[-1].MessageData
        if ($InputData -eq "FINISHED")
        {
            $Finished = $true
        }
        else
        {
            <# Do some stuff with the $InputData #>
            $ResultData += $ProgressedInputData

        }
        $WorkCounter++
    }
    Write-Information $ResultData
}
$PS = [PowerShell]::Create()
$PS.AddScript($Code) | Out-Null
$PS.AddArgument($PS) | Out-Null #Hand the PS to itself to make the streams accessible inside the thread
$Handle = $PS.BeginInvoke() | Out-Null

for ($i = 0; $i -lt 10; $i++)
{
    $PS.Streams.Information.Add([System.Management.Automation.InformationRecord]::new($i, ""))
    #I just gave my background thread some stuff to do without the need to instantiate a new one again
    #Now this thread can do some work too...
}
$PS.Streams.Information.Add([System.Management.Automation.InformationRecord]::new("FINISHED", ""))
$Handle.AsyncWaitHandle.WaitOne() #Wait for my background thread to finish all its work
$SomeReturnValue = $PS.Streams.Information[-1].MessageData

My actual question is: Is it possible, to access the current PowerShell instance without the need to hand it over like I did with $PS.AddArgument($PS)?



Solution 1:[1]

Let me offer an alternative to Mathias R. Jessen's helpful answer, based on the ThreadJob module's Start-ThreadJob cmdlet, which ships with PowerShell (Core) v6+ and in Windows PowerShell can be installed on demand (e.g., Install-Module ThreadJob -Scope CurrentUser)

As the name suggests, it offers thread-based background operations, as a faster and lighter-weight alternative to the child-process-based background jobs created by Start-Job (see this answer for a juxtaposition).

As such, it is a friendlier, higher-level alternative to managing multiple threads (runspaces) via the PowerShell SDK, allowing you to use the usual job-management cmdlets to interact with the background threads.

A simple example:

# Create a synchronized (thread-safe) queue.
$threadSafeQueue = [System.Collections.Concurrent.ConcurrentQueue[string]]::new()

# Start a thread job that keeps processing elements in the queue
# indefinitely, sleeping a little between checks for new elements.
# A special element value is used to signal that processing should end.
$jb = Start-ThreadJob {
  $q = $using:threadSafeQueue # get a reference to the thread-safe queue.
  $element = $null # variable to receive queue elements
  while ($true) {
    # Process all elements currently in the queue.
    while ($q.TryDequeue([ref] $element)) {
      # Check for the signal to quit, by convention a single NUL char. here.
      if ("`0" -eq $element) { 'Quitting...'; return }
      # Process the element at hand.
      # In this example, echo the dequeued element enclosed in "[...]"
      '[{0}]' -f $element
    }
    # Queue is (now) empty, sleep a little before checking for new elements.
    Start-Sleep -MilliSeconds 100
  }
}

# Populate the queue with the numbers from 1 to 10.
1..10 | ForEach-Object {
  $threadSafeQueue.Enqueue($_) # This triggers activity in the background thread.
  # Retrieve available output from the thread job.
  $jb | Receive-Job
}

# Send the quit signal, retrieve remaining output and delete the job.
$threadSafeQueue.Enqueue("`0")
$jb | Receive-Job -Wait -AutoRemoveJob

Output:

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
Quitting...

See also:

  • The PowerShell (Core) v7+ Foreach-Object -Parallel feature, which similarly uses threads to process pipeline input in parallel.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1