【问题标题】:How does threading in powershell work?Powershell中的线程如何工作?
【发布时间】:2022-03-30 20:17:59
【问题描述】:

我想在 powershell 中将一些文件解析操作与网络活动并行化。快速谷歌它, start-thread 看起来像是一个解决方案,但是:

“start-thread”一词未被识别为 cmdlet、函数、脚本文件或可运行程序的名称。检查名称的拼写,如果包含路径,请验证路径是否正确,然后重试。

当我尝试 start-job 时也发生了同样的事情。

我也试过摆弄System.Threading.Thread

[System.Reflection.Assembly]::LoadWithPartialName("System.Threading")
#This next errors, something about the arguments I can't figure out from the documentation of .NET
$tstart = new-object System.Threading.ThreadStart({DoSomething}) 
$thread = new-object System.Threading.Thread($tstart) 
$thread.Start()

所以,我认为最好是在我使用 start-thread 时知道我做错了什么,因为它似乎对其他人有用。我用的是v2.0,不需要向下兼容。

【问题讨论】:

  • tl;dr: receive-job (wait-job ($a = start-job { "heyo!" })); remove-job $a$a = start-job { "heyo!" }; wait-job $a; receive-job $a; remove-job $a 另请注意,如果您在作业完成之前致电 receive-job,您可能一无所获。跨度>
  • 还有(get-job $a).jobstateinfo.state;

标签: multithreading powershell


【解决方案1】:

Powershell 没有名为 Start-Thread 的内置命令。

但是,V2.0 确实有 PowerShell 作业,可以在后台运行,并且可以被视为等同于线程。您可以使用以下命令来处理作业:

Name                              Category  Synopsis
----                              --------  --------
Start-Job                         Cmdlet    Starts a Windows PowerShell background job.
Get-Job                           Cmdlet    Gets Windows PowerShell background jobs that are running in the current ...
Receive-Job                       Cmdlet    Gets the results of the Windows PowerShell background jobs in the curren...
Stop-Job                          Cmdlet    Stops a Windows PowerShell background job.
Wait-Job                          Cmdlet    Suppresses the command prompt until one or all of the Windows PowerShell...
Remove-Job                        Cmdlet    Deletes a Windows PowerShell background job.

这是一个关于如何使用它的示例。要启动作业,请使用 start-job 并传递一个包含您要异步运行的代码的脚本块:

$job  = start-job { get-childitem . -recurse }

此命令将启动一个作业,递归获取当前目录下的所有子项,您将立即返回命令行。

您可以检查$job 变量以查看作业是否已完成等。如果您想等待作业完成,请使用:

wait-job $job

最后,要接收作业的结果,请使用:

receive-job $job

【讨论】:

  • Start-Job 实际上启动了一个新的 Powershell 会话,而且它很重。它不会启动线程。
  • Start-Job 不捕获任何环境状态,没有本地变量、函数或模块导入,开销巨大,是典型的 PowerShell API。如果 .NET TPL 可以与一些调度到 PS 运行空间策略一起使用,那将非常有帮助
【解决方案2】:

你不能像这样直接使用线程,但你不能因为尝试而受到责备,因为一旦整个 BCL 摆在你面前,期望它的大部分工作并不是完全愚蠢的 :)

PowerShell 在管道中运行脚本块,而这些脚本块又需要运行空间来执行它们。前段时间,我在博客上写过如何为 v2 ctp3 滚动您自己的 MT 脚本,但技术(和 API)仍然相同。主要工具是[runspacefactory][powershell] 类型。看这里:

http://www.nivot.org/2009/01/22/CTP3TheRunspaceFactoryAndPowerShellAccelerators.aspx

以上是处理 MT 脚本的最轻量级的方法。 v2 中通过 start-job 和 get-job 提供后台作业支持,但我认为您已经发现了这一点,并且看到它们相当重量级。

【讨论】:

    【解决方案3】:

    最接近线程并且比作业更高效的是 PowerShell runspaces

    这是一个非常基本的例子:

    # the number of threads
    $count = 10
    
    # the pool will manage the parallel execution
    $pool = [RunspaceFactory]::CreateRunspacePool(1, $count)
    $pool.Open()
    
    try {        
        # create and run the jobs to be run in parallel
        $jobs = New-Object object[] $count
        for ($i = 0; $i -lt $count; $i++) {
            $ps = [PowerShell]::Create()
            $ps.RunspacePool = $pool
    
            # add the script block to run
            [void]$ps.AddScript({
                param($Index)
                Write-Output "Index: $index"
            })
    
            # optional: add parameters
            [void]$ps.AddParameter("Index", $i)
    
            # start async execution
            $jobs[$i] = [PSCustomObject]@{
                PowerShell = $ps
                AsyncResult = $ps.BeginInvoke()
            }
        }
        foreach ($job in $jobs) {
            try {
                # wait for completion
                [void]$job.AsyncResult.AsyncWaitHandle.WaitOne()
    
                # get results
                $job.PowerShell.EndInvoke($job.AsyncResult)
            }
            finally {
                $job.PowerShell.Dispose()
            }
        }
    }
    finally {
        $pool.Dispose()
    }
    

    它还可以让你做更高级的事情,比如

    • 限制池中并行运行空间的数量
    • 从当前会话中导入函数和变量

    等等

    【讨论】:

      【解决方案4】:

      answer,现在很简单。

      Install-Module -Name ThreadJob -Confirm:$true
      
      $j1= Start-ThreadJob  `
                      -FilePath $YourThreadJob `
                      -ArgumentList @("A","B")
      $j1|get-job
      $j1|receive-job
      

      【讨论】:

      • :D 9 年 7 个月。那么这有什么作用呢?用不同的参数启动同一个脚本的子shell?
      • 是的,您可以根据需要启动任意数量的线程,当然不仅仅是调用自己(我的原始示例递归严重)
      猜你喜欢
      • 2013-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-30
      • 1970-01-01
      相关资源
      最近更新 更多