为什么启动线程job调用该函数?

发布于 2025-01-30 00:37:03 字数 1563 浏览 5 评论 0原文

我想通过脚本备份一些数据。所有数据均应在单独的线程中拉链。但是两件事出了问题:

  1. 函数testfct从未被调用 - 指示:否“内部:...”。
  2. 缺少参数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
}

脚本有什么问题?

I want to backup some data over my script. All data shall be zipped in a separate thread. But two things go wrong:

  1. The function testFct is never called - indication: no "inside: ...".
  2. The parameter ZipSource is missing - see output.

Calling the script results in:

> .\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
>

Here is my Script:

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
}

What is wrong with the script?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

甜点 2025-02-06 00:37:03

由于testfct在线程job的范围中不存在,因此您需要首先存储函数的定义,然后将其传递到Runspace范围并定义该功能,如此答案

另一个问题是尝试多次引用相同的$输入。由于自动变量$ input ,您只能在 script block

由于$ input是枚举者,因此访问其任何属性原因$ input不再可用。您可以将$输入存储在另一个变量中以重复使用$ input properties。

作为解决方法,您可以将变量包裹在 array subxpression operator @() noreflow noreferrer“> subexpression opertress” >将枚举的输出存储在新变量中。

这是上面解释的一个简单示例:

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

最后,您的脚本实际上不是多线程,这是因为您将作业存储在循环中,然后顺序等待而不是立即开始所有工作,然后等待所有工作

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

Since testFct doesn't exist in the scope of your ThreadJob, you need to first store the function's definition and then pass it to the runspace scope and define the function there as demonstrated in this answer.

The other issue is trying to reference the same $input more than one time. Due to the nature of the automatic variable $input, you can only reference this variable once in your script block:

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.

As a workaround you could wrap the variable in the Array subexpression operator @( ) or the Subexpression operator $( ) to store the enumerated output in a new variable.

Here is a simple example of what's explained above:

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

Lastly, your script is not actually multi-threading, this is because you're storing the job inside your loop and then waiting for it sequentially instead of starting all jobs at once and then waiting for all of them.

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

The output you can expect from this:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文