首页 > 解决方案 > 如何在 ForEach-Object -Parallel 中传递自定义函数

问题描述

我找不到传递函数的方法。只是变量。

没有将函数放在 ForEach 循环中的任何想法?

function CustomFunction {
    Param (
        $A
    )
    Write-Host $A
}

$List = "Apple", "Banana", "Grape" 
$List | ForEach-Object -Parallel {
    Write-Host $using:CustomFunction $_
}

在此处输入图像描述

标签: powershellpowershell-core

解决方案


解决方案并不像人们希望的那样简单:

function CustomFunction {
    Param ($A)
    "[$A]"
}

# Get the function's definition *as a string*
$funcDef = $function:CustomFunction.ToString()

"Apple", "Banana", "Grape"  | ForEach-Object -Parallel {
    # Define the function inside this thread...
    $function:CustomFunction = $using:funcDef
    # ... and call it.
    CustomFunction $_
}

注意:此答案包含一个类似的解决方案,用于在脚本块中使用调用者范围内的ForEach-Object -Parallel脚本块。

  • 注意:如果您的函数是在模块中定义的,该模块位于模块自动加载功能已知的位置之一,您的函数调用将按原样工作ForEach-Object -Parallel,无需额外的努力 - 但每个线程都会产生(隐式) 导入模块。

  • 上述方法是必要的,因为除了当前位置(工作目录)和环境变量(适用于进程范围)之外,ForEach-Object -Parallel创建的线程看不到调用者的状态,尤其是变量和函数(以及不是定制的 PS 驱动器和导入的模块)。

    • 更新:js2010 的有用答案显示了一个更直接的解决方案,它通过 aSystem.Management.Automation.FunctionInfo获得Get-Command,可以直接使用&. 唯一需要注意的是,原始函数应该没有副作用,即应该完全基于参数或管道输入进行操作,而不依赖于调用者的状态,尤其是它的变量,因为这可能会导致线程安全问题。上面的字符串化技术隐式地防止了对调用者状态的任何有问题的引用,因为函数体是在每个线程的上下文中重建的。
  • 从 PowerShell 7.1 开始,GitHub 上正在讨论一项增强功能,以支持按需将调用者的状态复制到线程,这将使调用者的函数可用。

请注意,没有辅助就凑合。$funcDef变量并尝试重新定义函数$function:CustomFunction = $using:function:CustomFunction很诱人,但$function:CustomFunction它是一个脚本块,并且明确禁止使用带有$using:范围说明符的脚本块。

  • 但是,$function:CustomFunction = $using:function:CustomFunction 可以Start-Job; 例如,请参阅此答案

  • 它也适用于Start-ThreadJob, 你甚至可以这样做& $using:function:CustomFunction $_,因为$using:function:CustomFunction它被保存为脚本块(与 不同Start-Job,它被反序列化为string,这本身就是令人惊讶的行为 - 请参阅GitHub 问题 #11698)。但是,尚不清楚design是否支持此行为,因为它受到上述相同的潜在跨线程问题的影响。

$function:CustomFunction命名空间变量表示法的一个实例,它允许您通过分配包含函数体的 a 或字符串来获取函数(它的主体作为[scriptblock]实例)和设置(定义)它。[scriptblock]


推荐阅读