首页 > 解决方案 > 运行 ForEach-Object -Parallel,导出数据丢失

问题描述

我有一些工作代码,基本上查询 2 个不同的 Graph API 端点,User Principal Name column然后extension_335d4df9847945fbaa472c8b8fbb5d75_employeeNumber在最近从我们的现场广告扩展。

这段代码工作得很好,但是如果我尝试并行化它,我在extension_335d4df9847945fbaa472c8b8fbb5d75_employeeNumber列中没有得到任何结果。

这是因为一旦它被并行化,我就无法在并行进程之间共享变量?如果是这样,我到底如何做到这一点?

下面的代码 - 如果你删除 -Parallel,它工作正常:

$graphApiUri = "https://graph.microsoft.com/v1.0/reports/getOffice365ActiveUserDetail(period='D90')"

$Uri = "https://graph.microsoft.com/v1.0/users?`$select=userPrincipalName,extension_335d4df9847945fbaa472c8b8fbb5d75_employeeNumber"

$O365Report = Invoke-RestMethod -Method Get -Uri $graphApiUri -Headers $headerParams | ConvertFrom-Csv

# If the result is more than 999, we need to read the @odata.nextLink to show more than one side of users
$UserDetails = while (-not [string]::IsNullOrEmpty($uri)) {
    # API Call
    $apiCall = try {
        Invoke-RestMethod -Headers $headerParams -Uri $uri -Method Get
    }
    catch {
        $errorMessage = $_.ErrorDetails.Message | ConvertFrom-Json
    }
    $uri = $null
    if ($apiCall) {
        # Check if any data is left
        $uri = $apiCall.'@odata.nextLink'
        $apiCall
    }
}

Write-Output "Matching UPN to employeeNumber..."

$O365Report | ForEach-Object -Parallel {
    $CurrentEmpNumber = $UserDetails.value |
        Where-Object userPrincipalName -eq $_.'User Principal Name' |
            Select-Object -ExpandProperty extension_335d4df9847945fbaa472c8b8fbb5d75_employeeNumber -ErrorAction SilentlyContinue
    $_ | Add-Member -MemberType NoteProperty -Name extension_335d4df9847945fbaa472c8b8fbb5d75_employeeNumber -Value $CurrentEmpNumber
}

$O365Report | Export-Csv $ReportCSV -NoTypeInformation
Write-Output "Report saved to $ReportCSV."

标签: powershellparallel-processingmicrosoft-graph-apioffice365

解决方案


在脚本块内,并且您尝试引用在其外部创建的变量时,您需要ForEach-Object -Parallel在变量名前加上using:$using:UserDetails

例子:

不返回任何内容,因为$test在并行脚本块的范围内无法访问:

$test = 1;
0..5 | % -Parallel { $test; };

返回$test五次的值,因为通过使用$using:test您现在可以看到它的值:

$test = 1;
0..5 | % -Parallel { $using:test; };

从文档:

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.1

ForEach-Object -Parallel 参数集在单独的进程线程上并行运行脚本块。$using: 关键字允许将变量引用从 cmdlet 调用线程传递到每个正在运行的脚本块线程。由于脚本块在不同的线程中运行,因此必须安全地使用通过引用传递的对象变量。一般来说,从不改变的引用对象中读取是安全的。但是如果正在修改对象状态,那么您必须使用线程安全对象,例如 .Net System.Collection.Concurrent 类型(参见示例 11)。


个人备注:

我还建议使用-ThrottleLimit来限制其最大并行度。默认值为 5,但您可能需要更多或更少,具体取决于测试。


推荐阅读