Skip to content

PowerShell class instance methods cannot run concurrently #3651

@PaulHigin

Description

@PaulHigin

This behavior changed between PowerShell versions 5.0 and 5.1. The change was to make PowerShell class method script blocks bound to the runspace in which they were created. If a class instance method is run from within a different runspace then it gets "marshaled" back to the original runspace (via engine events). The result is that if a single class instance is shared among multiple runspaces (e.g., a runspace pool) with the intention to execute instance methods concurrently, the methods run serially instead.

I don't know why this changed between 5.0 and 5.1 but it looks to be by design. @vors can you please comment?

Repro steps:

PS > (measure-command { .\RunspaceInstanceTest.ps1 }).TotalSeconds

RunspaceInstanceTest.ps1 file script:
class Writer
{
    static WriteLineS([string] $Msg)
    {
        for ($i=0; $i -lt 10; $i++)
        {
            [Console]::WriteLine("Static: Loop $i - $Msg")
            Start-Sleep -MilliSeconds 100
        }
    }

    WriteLineI([string] $msg)
    {
        for ($i=0; $i -lt 10; $i++)
        {
            [Console]::WriteLine("Instance: Loop $i - $Msg")
            Start-Sleep -MilliSeconds 100
        }
    }
}

$script = @'
    param ([object] $writerInstance, [string] $Title)

    class Writer
    {
        static WriteLineS([string] $Msg)
        {
            for ($i=0; $i -lt 10; $i++)
            {
                [Console]::WriteLine("Static: Loop $i - $Msg")
                Start-Sleep -MilliSeconds 100
            }
        }

        WriteLineI([string] $msg)
        {
            for ($i=0; $i -lt 10; $i++)
            {
                [Console]::WriteLine("Instance: Loop $i - $Msg")
                Start-Sleep -MilliSeconds 100
            }
        }
    }

    # Create new instance
    #$writerInstance = [Writer]::new()

    # Instance write
    $writerInstance.WriteLineI("$Title")

    # Static write
    #[Writer]::WriteLineS("$Title")
'@

$writer = [Writer]::new()

$rsp = [runspacefactory]::CreateRunspacePool(1, 10, $host)
$rsp.Open()

class Task
{
    [powershell] $powershell
    [System.IAsyncResult] $Async
}

$tasks = @()

1..5 | foreach {

    $task = [Task]::new()
    $tasks += $task

    $task.powershell = [powershell]::Create()
    $task.powershell.RunspacePool = $rsp
    $task.Async = $task.powershell.AddScript($script).AddArgument($writer).AddArgument("Task $_").BeginInvoke()
}

foreach ($task in $tasks)
{
    $task.powershell.EndInvoke($task.Async)
    $task.powershell.Dispose()
}

$rsp.Dispose()

Expected:
5 concurrently running 1 second loops (with sleep) should take about 1 second to run.

Actual Result:
It takes about 5 seconds to run indicating that the concurrent scripts are running serially.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugResolution-FixedThe issue is fixed.WG-Enginecore PowerShell engine, interpreter, and runtime

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions