为什么 Start-ThreadJob 没有调用该函数?
Why is the function not called by Start-ThreadJob?
我想通过我的脚本备份一些数据。所有数据都应压缩在一个单独的线程中。但是有两件事出了问题:
- 从未调用函数 testFct - 指示:没有“内部:...”。
- 缺少 ZipSource 参数 - 查看输出。
调用脚本结果:
> .\Backup.ps1
outside: -What: Data A -ZipSource -ZipDest C:\Users\xyz\AppData\Local\Temp -Timestamp "20220517-002854
outside: -What: Data B -ZipSource -ZipDest C:\Users\xyz\AppData\Local\Temp -Timestamp "20220517-002854
>
这是我的脚本:
class BackupContentData
{
[ValidateNotNullOrEmpty()][string]$What
[ValidateNotNullOrEmpty()][string]$ZipSource
}
$bcd = @(
[BackupContentData]@{ What="Data A"; ZipSource="$env:USERPROFILE\Documents\a_file.txt";}
[BackupContentData]@{ What="Data B"; ZipSource="$env:USERPROFILE\Documents\b_file.txt";}
)
function testFct {
param([string]$What, [string]$ZipSource, [string]$ZipDest, [string]$Timestamp)
Write-Host "inside: -What: "$What" -ZipSource "$ZipSource" -ZipDest "$ZipDest" -Timestamp "$Timestamp
}
$timestamp="$(get-date -f yyyyMMdd-HHmmss)"
foreach ($e in $bcd) {
$job = Start-ThreadJob -Name $e.What -InputObject $e -ScriptBlock {
Invoke-Expression "function getTest {$using:testFct}"
Write-Host "outside: -What: "$input.What" -ZipSource "$input.ZipSource" -ZipDest "$env:Temp" -Timestamp ""$(get-date -f yyyyMMdd-HHmmss)"
getTest -What "$input.What" -ZipSource "$input.ZipSource" -ZipDest "$env:Temp" -Timestamp "$(get-date -f yyyyMMdd-HHmmss)"
}
Receive-Job $job -AutoRemoveJob -Wait
}
脚本有什么问题?
由于 testFct
不存在于您的 ThreadJob 范围内,您需要首先存储函数的定义,然后将其传递到运行空间范围并在那里定义函数,如 [=23= 中所示].
另一个问题是试图多次引用同一个 $input
。由于 automatic variable $input
的性质,您只能在 脚本块中引用此变量一次 :
Since $input
is an enumerator, accessing any of its properties causes $input
to no longer be available. You can store $input
in another variable to reuse the $input
properties.
作为解决方法,您可以将变量包装在 Array subexpression operator @( )
or the Subexpression operator $( )
中以将枚举输出存储在新变量中。
这是上面解释的一个简单示例:
Start-ThreadJob -InputObject 'Hello World' -ScriptBlock {
"1. $input"
"2. $input"
} | Receive-Job -AutoRemoveJob -Wait
# This outputs:
# 1. Hello World
# 2.
# And the workaround
Start-ThreadJob -InputObject 'Hello World' -ScriptBlock {
# This would also work:
# $thisInput = foreach($i in $input) { $i }
$thisInput = $($input)
"1. $thisInput"
"2. $thisInput"
} | Receive-Job -AutoRemoveJob -Wait
# Outputs:
# 1. Hello World
# 2. Hello World
最后,您的脚本实际上并不是 multi-threading,这是因为您将作业存储在循环中,然后依次等待它 而不是一次启动所有作业然后等待所有作业。
class BackupContentData {
[ValidateNotNullOrEmpty()] [string] $What
[ValidateNotNullOrEmpty()] [string] $ZipSource
}
function testFct {
param([string]$What, [string]$ZipSource, [string]$ZipDest, [string]$Timestamp)
Write-Host "inside: -What: $What -ZipSource $ZipSource -ZipDest $ZipDest -Timestamp $Timestamp"
}
# definition of the function is stored here
$def = ${function:testFct}.ToString()
$bcd = @(
[BackupContentData]@{ What="Data A"; ZipSource="$env:USERPROFILE\Documents\a_file.txt" }
[BackupContentData]@{ What="Data B"; ZipSource="$env:USERPROFILE\Documents\b_file.txt" }
)
$job = foreach ($e in $bcd) {
Start-ThreadJob -Name $e.What -InputObject $e -ScriptBlock {
$thisObject = $($input)
# Define a new function with name `getTest` in this scope using `testFct` definition
${function:getTest} = $using:def
Write-Host "outside: -What: $($thisObject.What) -ZipSource $($thisObject.ZipSource) -ZipDest $env:Temp -Timestamp $(get-date -f yyyyMMdd-HHmmss)"
getTest -What $thisObject.What -ZipSource $thisObject.ZipSource -ZipDest $env:Temp -Timestamp (Get-Date -f yyyyMMdd-HHmmss)
}
}
$job | Receive-Job -AutoRemoveJob -Wait
您可以从中得到的输出:
outside: -What: Data A -ZipSource C:\Users\user\Documents\a_file.txt -ZipDest C:\Users\user\AppData\Local\Temp -Timestamp 20220516-202251
inside: -What: Data A -ZipSource C:\Users\user\Documents\a_file.txt -ZipDest C:\Users\user\AppData\Local\Temp -Timestamp 20220516-202251
outside: -What: Data B -ZipSource C:\Users\user\Documents\b_file.txt -ZipDest C:\Users\user\AppData\Local\Temp -Timestamp 20220516-202251
inside: -What: Data B -ZipSource C:\Users\user\Documents\b_file.txt -ZipDest C:\Users\user\AppData\Local\Temp -Timestamp 20220516-202251
我想通过我的脚本备份一些数据。所有数据都应压缩在一个单独的线程中。但是有两件事出了问题:
- 从未调用函数 testFct - 指示:没有“内部:...”。
- 缺少 ZipSource 参数 - 查看输出。
调用脚本结果:
> .\Backup.ps1
outside: -What: Data A -ZipSource -ZipDest C:\Users\xyz\AppData\Local\Temp -Timestamp "20220517-002854
outside: -What: Data B -ZipSource -ZipDest C:\Users\xyz\AppData\Local\Temp -Timestamp "20220517-002854
>
这是我的脚本:
class BackupContentData
{
[ValidateNotNullOrEmpty()][string]$What
[ValidateNotNullOrEmpty()][string]$ZipSource
}
$bcd = @(
[BackupContentData]@{ What="Data A"; ZipSource="$env:USERPROFILE\Documents\a_file.txt";}
[BackupContentData]@{ What="Data B"; ZipSource="$env:USERPROFILE\Documents\b_file.txt";}
)
function testFct {
param([string]$What, [string]$ZipSource, [string]$ZipDest, [string]$Timestamp)
Write-Host "inside: -What: "$What" -ZipSource "$ZipSource" -ZipDest "$ZipDest" -Timestamp "$Timestamp
}
$timestamp="$(get-date -f yyyyMMdd-HHmmss)"
foreach ($e in $bcd) {
$job = Start-ThreadJob -Name $e.What -InputObject $e -ScriptBlock {
Invoke-Expression "function getTest {$using:testFct}"
Write-Host "outside: -What: "$input.What" -ZipSource "$input.ZipSource" -ZipDest "$env:Temp" -Timestamp ""$(get-date -f yyyyMMdd-HHmmss)"
getTest -What "$input.What" -ZipSource "$input.ZipSource" -ZipDest "$env:Temp" -Timestamp "$(get-date -f yyyyMMdd-HHmmss)"
}
Receive-Job $job -AutoRemoveJob -Wait
}
脚本有什么问题?
由于 testFct
不存在于您的 ThreadJob 范围内,您需要首先存储函数的定义,然后将其传递到运行空间范围并在那里定义函数,如 [=23= 中所示].
另一个问题是试图多次引用同一个 $input
。由于 automatic variable $input
的性质,您只能在 脚本块中引用此变量一次 :
Since
$input
is an enumerator, accessing any of its properties causes$input
to no longer be available. You can store$input
in another variable to reuse the$input
properties.
作为解决方法,您可以将变量包装在 Array subexpression operator @( )
or the Subexpression operator $( )
中以将枚举输出存储在新变量中。
这是上面解释的一个简单示例:
Start-ThreadJob -InputObject 'Hello World' -ScriptBlock {
"1. $input"
"2. $input"
} | Receive-Job -AutoRemoveJob -Wait
# This outputs:
# 1. Hello World
# 2.
# And the workaround
Start-ThreadJob -InputObject 'Hello World' -ScriptBlock {
# This would also work:
# $thisInput = foreach($i in $input) { $i }
$thisInput = $($input)
"1. $thisInput"
"2. $thisInput"
} | Receive-Job -AutoRemoveJob -Wait
# Outputs:
# 1. Hello World
# 2. Hello World
最后,您的脚本实际上并不是 multi-threading,这是因为您将作业存储在循环中,然后依次等待它 而不是一次启动所有作业然后等待所有作业。
class BackupContentData {
[ValidateNotNullOrEmpty()] [string] $What
[ValidateNotNullOrEmpty()] [string] $ZipSource
}
function testFct {
param([string]$What, [string]$ZipSource, [string]$ZipDest, [string]$Timestamp)
Write-Host "inside: -What: $What -ZipSource $ZipSource -ZipDest $ZipDest -Timestamp $Timestamp"
}
# definition of the function is stored here
$def = ${function:testFct}.ToString()
$bcd = @(
[BackupContentData]@{ What="Data A"; ZipSource="$env:USERPROFILE\Documents\a_file.txt" }
[BackupContentData]@{ What="Data B"; ZipSource="$env:USERPROFILE\Documents\b_file.txt" }
)
$job = foreach ($e in $bcd) {
Start-ThreadJob -Name $e.What -InputObject $e -ScriptBlock {
$thisObject = $($input)
# Define a new function with name `getTest` in this scope using `testFct` definition
${function:getTest} = $using:def
Write-Host "outside: -What: $($thisObject.What) -ZipSource $($thisObject.ZipSource) -ZipDest $env:Temp -Timestamp $(get-date -f yyyyMMdd-HHmmss)"
getTest -What $thisObject.What -ZipSource $thisObject.ZipSource -ZipDest $env:Temp -Timestamp (Get-Date -f yyyyMMdd-HHmmss)
}
}
$job | Receive-Job -AutoRemoveJob -Wait
您可以从中得到的输出:
outside: -What: Data A -ZipSource C:\Users\user\Documents\a_file.txt -ZipDest C:\Users\user\AppData\Local\Temp -Timestamp 20220516-202251
inside: -What: Data A -ZipSource C:\Users\user\Documents\a_file.txt -ZipDest C:\Users\user\AppData\Local\Temp -Timestamp 20220516-202251
outside: -What: Data B -ZipSource C:\Users\user\Documents\b_file.txt -ZipDest C:\Users\user\AppData\Local\Temp -Timestamp 20220516-202251
inside: -What: Data B -ZipSource C:\Users\user\Documents\b_file.txt -ZipDest C:\Users\user\AppData\Local\Temp -Timestamp 20220516-202251