0

I am trying to work out an efficient way of calling a Powershell cmdlet for 20-30 files asynchronously. Although the below code is working, the Import-Module step is run for every file which is processed. Unfortunately this Module takes between 3 or 4 seconds to import.

Searching on the web I can find references to RunspacePools & InitialSessionState, but have had issues trying to create a PSHost object which is required in the CreateRunspacePool overload.

Any help would be appreciated.

Thanks

Gavin

.

.

Code sample from my application:

I am using a Parallel ForEach to distribute the files between threads.

Parallel.ForEach(files, (currentFile) => 
{
    ProcessFile(currentfile);
});



private void ProcessFile(string filepath)
{
    // 
    // Some non powershell related code removed for simplicity
    //


    // Start PS Session, Import-Module and Process file
    using (PowerShell PowerShellInstance = PowerShell.Create())
    {
        PowerShellInstance.AddScript("param($path) Import-Module MyModule; Process-File -Path $path");
        PowerShellInstance.AddParameter("path", filepath);
        PowerShellInstance.Invoke();
    }
}
Gavin
  • 1
  • 1
  • Possible duplicate of [PowerShell - How to Import-Module in a Runspace](http://stackoverflow.com/questions/6266108/powershell-how-to-import-module-in-a-runspace) – user4317867 Jun 11 '16 at 22:48
  • Try using the code from [here](https://communary.net/2014/11/24/runspaces-made-simple/) to load a Module using the InitialSessionState for the RunSpacePools. – user4317867 Jun 12 '16 at 00:39
  • Thanks, the answer on the post still imports the module once per item in the foreach loop. Although there is a link to a blog which describes the information i needed. So thanks for pointing me in the right direction. – Gavin Jun 12 '16 at 11:49
  • @user4317867 - Thanks for the link, but this shows PowerShell code which also uses `CreateRunspacePool($minRunspaces, $maxRunspaces, $iss, $Host)` and i still dont know how to create a $Host variable to apply. – Gavin Jun 12 '16 at 11:53
  • It seems `$Host` is TypeName: System.Management.Automation.Internal.Host.InternalHost that's created when the Powershell console is opened. Are you looking to create a pool of RunSpaces that all have the desired module loaded? – user4317867 Jun 12 '16 at 19:48
  • This [question](http://stackoverflow.com/questions/24143196/powershell-how-to-keep-imported-modules-loaded-across-sessions) looks to have a great sample for creating a RunSpacePool and loading modules into the InitialSessionState. – user4317867 Jun 12 '16 at 20:00

1 Answers1

0

As it has already been explained in the comments, this won't work with PSJobs because objects are serialized and the jobs themselves run in a separate process.

What you can do is create a RunspacePool with an InitialSessionState that has the module imported:

private RunspacePool rsPool;

public void ProcessFiles(string[] files)
{
    // Set up InitialSessionState 
    InitialSessionState initState = InitialSessionState.Create();
    initState.ImportPSModule(new string[] { "MyModule" });
    initState.LanguageMode = PSLanguageMode.FullLanguage;

    // Set up the RunspacePool
    rsPool = RunspaceFactory.CreateRunspacePool(initialSessionState: initState);
    rsPool.SetMinRunspaces(1);
    rsPool.SetMaxRunspaces(8);
    rsPool.Open();

    // Run ForEach()
    Parallel.ForEach(files, ProcessFile);
}

private void ProcessFile(string filepath)
{
    // Start PS Session and Process file
    using (PowerShell PowerShellInstance = PowerShell.Create())
    {
        // Assign the instance to the RunspacePool
        PowerShellInstance.RunspacePool = rsPool;

        // Run your script, MyModule has already been imported
        PowerShellInstance.AddScript("param($path) Process-File @PSBoundParameters").AddParameter("path", filepath);
        PowerShellInstance.Invoke();
    }
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206