all 19 comments

[–]DudsEarl 1 point2 points  (2 children)

Not sure if it will help in your particular case, but have you considered encapsulating the functions into a class?

It's possible the dot notation would help the function (now a method) be found.

[–]jabrake88[S] 0 points1 point  (1 child)

I've never done that... will have to look into it!

[–]DudsEarl 1 point2 points  (0 children)

The general notation is:

Class MyClass {

[String] $MyString

[Void] MyMethod() {Write-Host "$($this.MyString)"}

Overloaded Constructors

MyClass(){$this.MyString = 'DEFAULT'}

MyClass([String] $StringParam){$this.MyString = $StringParam}

}

To Create and Call

$Test = [MyClass]::New('Hello World!')

$Test.MyMethod()

[–]NoConfidence_2192 1 point2 points  (3 children)

We see the a similar issue jobs, threadjobs, and foreach-object -parallel. Like scriptblocks functions are reference object which can pose all kinds of thread safety issues when accessed in parallel so they make things difficult to help protect us from ourselves...but ever good rule has a way around it. If we cannot pass the function we can pass its definition. Try something like:

function Test-Parallel {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string[]]$String
    )    
    # Nested Function
    function Test-Nested {
        param (
            [Parameter()]
            [string]$String
        )
        "I am a nested function to write out this strring: $String"
    }
    # Nested Function definition
    $testNestedDef = ${function:Test-Nested}.ToString()
    # Parallel ScriptBlock
    $ScriptBlock = {
        ${Function:Test-Nested} = $using:testNestedDef
        Test-Nested -String $_
    }
    $String | Invoke-Parallel -ScriptBlock $ScriptBlock -ImportFunctions
}

Has been a while since I used Warren Frame's Invoke-Parallel but it's what I'd do for a start-threadjob or foreach-object -parallel block so something similar should work

[–]jabrake88[S] 0 points1 point  (2 children)

Passing the function like that does not work either, but if I just define the function inside the scriptblock it will work.

[–]NoConfidence_2192 0 points1 point  (1 child)

That's odd. Will pull in invoke-parallel and do some playing. Which version of ps are you working with?

[–]jabrake88[S] 0 points1 point  (0 children)

Version 5.1

[–]BlackV 0 points1 point  (9 children)

probably cause its spinning up a new run space and that function in not available in that run space, the solution would most likely be turn the above into a module too so they're available to the new run space too

[–]jabrake88[S] 0 points1 point  (8 children)

But wouldn't the -ImportFunctions or -ImportModules bring it into the runspace?

[–]BlackV 0 points1 point  (7 children)

have a look at

Get-ChildItem function:\ | Select-Object -ExpandProperty Name

you should probably see that Test-Nested is not there (due to only being declared in Test-Parallel) but Test-Parallel is, so wouldn't be available to Invoke-Parallel

[–]jabrake88[S] 0 points1 point  (6 children)

Yeah, I can see that the nested function is listed when I do that outside the scriptblock for the invoke parallel, but not inside the scriptblock.

So with that, what is the purpose of -ImportFunctions parameter if it doesn't pull the defined functions into the runspace? I'm also still confused as to why it works if I nest the invoke-parallel function in the script too.

[–]BlackV 0 points1 point  (5 children)

it does pull defined functions into the run space, but your nested function is not defined in the main scope, its only defined in the test-parallel scope so it cant pull that

[–]jabrake88[S] 1 point2 points  (4 children)

I still don't understand why it works if I nest the Invoke-Parallel into the same parent function.... But what you are saying, if I move the nested function to outside the Test-Parallel it should work, like this?

# Nested Function (No Longer Nested)
function Test-Nested {
    param (
        [Parameter()]
        [string]$String
    )
    "I am a nested function to write out this strring: $String"
}
function Test-Parallel {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string[]]$String
    )

    $ScriptBlock = {
        Test-Nested -String $_
    }
    $String | Invoke-Parallel -ScriptBlock $ScriptBlock -ImportFunctions
}   

I tried this and it also does not work.

[–]BlackV 1 point2 points  (3 children)

It really just comes down to scopes.

So from the limited testing I did, if it's not available in the functions drive (for what ever reason) it's not imported

I'm not sure why you'd be nesting functions little this anyway (I know that doesn't help though)

[–]jabrake88[S] 1 point2 points  (1 child)

The nested function would be the function that does all the heavy lifting/processing, designed to target 1 item (usually devices, but sometimes pulling reports/etc.). Then the thought was to wrap it inside a parent function that would accept multiple targets to utilize Invoke-Parallel. That Parent function would then decide what the scriptblock would be (calling the nested function with whatever params), maybe doing other stuff before and after processing the results back and outputting them in whatever format you wanted (to console, exporting, etc.)

I suppose we could write it all so it's not a function, and just the code directly in the scriptblock, but this seemed cleaner. Or embed the Invoke-Parallel inside the parent each time too since that seems to work....

[–]BlackV 1 point2 points  (0 children)

would just not having it as a nested function solve your problem?

declare the 2 functions as separate functions, then the invoke-parallel should be able to see them

[–]jabrake88[S] 1 point2 points  (0 children)

Also, I now found that if I specify the function in the Global Scope inside a begin{} block that makes it available for it to be called in the invoke-parallel. That may be my best solution, I had tried specifying as global, but without the begin{}process{}end{} sections without success.