多线程帮助w / Powershell

时间:2010-07-13 17:10:45

标签: sql powershell

所以我有一个脚本将通过SQL Server中存储的列表中的所有服务器进行ping操作。该脚本工作正常,但它按顺序完成(跛脚)。

有人可以帮我解决一下如何使用多线程而不是foreach循环来改变它吗?

    $Server = "ServerName"
$Database = "DatabaseName"

$con = "server=$Server;database=$Database;Integrated Security=sspi"
$cmd = "SELECT ServerName FROM dbo.vwServerListActive"

  $da = new-object System.Data.SqlClient.SqlDataAdapter ($cmd, $con)

  $dt = new-object System.Data.DataTable

  $da.fill($dt) | out-null


  foreach ($srv in $dt)
    {

    $ping = new-object System.Net.NetworkInformation.Ping
    $Reply = $ping.send($srv.ServerName)

    $ServerName = $srv.ServerName 

    $ServerName
    $Reply.status

    if ($Reply.status –eq “Success”)
    {
        $sql = "UPDATE dbo.ServerList SET GoodPing = 1 WHERE GoodPing <> 1 AND ServerName = '$ServerName'"

    }
    else
    {
        $sql = "UPDATE dbo.ServerList SET GoodPing = 0 WHERE GoodPing <> 0 AND ServerName = '$ServerName'"
    }

    $Reply = ""

    invoke-sqlcmd -serverinstance $Server -database $Database -query $sql


    }

7 个答案:

答案 0 :(得分:4)

(根据Chad Miller的建议+限制要求+等待工作修正+ STA修复编辑)

<强> Support.ps1

powershell -File "Main.ps1" -Sta

<强> Main.ps1

$Server = "ServerName"   
$Database = "DatabaseName"   

$con = "server=$Server;database=$Database;Integrated Security=sspi"   
$cmd = "SELECT ServerName FROM dbo.vwServerListActive"   

$da = New-Object System.Data.SqlClient.SqlDataAdapter -ArgumentList $cmd, $con  

$dt = New-Object System.Data.DataTable   

$da.Fill($dt) | Out-Null  

$ThrottleLimit = 10 
$activeJobs = New-Object 'System.Collections.Generic.List[Int32]' 

$JobStateChanged = { 
    param ( 
        [System.Object]$Sender, 
        [System.Management.Automation.JobStateEventArgs]$EventArgs 
    ) 

    switch ($EventArgs.JobStateInfo.State) 
    { 
        Blocked { return } 
        Completed { $activeJobs.Remove($Sender.Id); break } 
        Failed { $activeJobs.Remove($Sender.Id); break } 
        NotStarted { return } 
        Running { return } 
        Stopped { $activeJobs.Remove($Sender.Id); break } 
    }

    Unregister-Event -SourceIdentifier ("{0}.StateChanged" -f $Sender.Name)
} 

foreach ($srv in $dt)   
{ 
    while ($true) 
    { 
        if ($activeJobs.Count -lt $ThrottleLimit) 
        { 
            $job = Start-Job -InitializationScript {   
                Add-PSSnapin -Name SqlServerCmdletSnapin100   
            } -ScriptBlock {  
                param (  
                    [String]$Server,  
                    [String]$Database,  
                    [String]$ServerName  
                )  

                if (Test-Connection -ComputerName $ServerName -Quiet)   
                {   
                    $sql = "UPDATE dbo.ServerList SET GoodPing = 1 WHERE GoodPing <> 1 AND ServerName = '$ServerName'"  
                }   
                else   
                {   
                    $sql = "UPDATE dbo.ServerList SET GoodPing = 0 WHERE GoodPing <> 0 AND ServerName = '$ServerName'"  
                }  

                Invoke-SqlCmd -ServerInstance $Server -Database $Database -Query $sql  
            } -ArgumentList $Server, $Database, $srv.ServerName  

            $activeJobs.Add($job.Id) 

            Register-ObjectEvent -InputObject $job -EventName StateChanged -SourceIdentifier ("{0}.StateChanged" -f $job.Name) -Action $JobStateChanged 

            break 
        } 
    } 
} 

Get-Job | Where-Object { $_.State -eq "Running" } | Wait-Job
Get-Job | Remove-Job

答案 1 :(得分:2)

如果有PowerShell 2.0,您可以使用后台作业。您需要将服务器列表分解为“组”。给定具有serverName和groupName的源表:

CREATE TABLE [dbo].[vwServerListActive](
    [serverName] [varchar](50) NULL,
    [groupName] [char](1) NULL
) 

对您的脚本稍作修改(另存为forum.ps1):

param($groupName)

$Server = "$env:computername\sql2k8"
$Database = "dbautility" 

$con = "server=$Server;database=$Database;Integrated Security=sspi" 
$cmd = "SELECT ServerName FROM dbo.vwServerListActive WHERE groupName ='$groupName'" 

  $da = new-object System.Data.SqlClient.SqlDataAdapter ($cmd, $con) 

  $dt = new-object System.Data.DataTable 

  $da.fill($dt) | out-null 


  foreach ($srv in $dt) 
    { 

    $ping = new-object System.Net.NetworkInformation.Ping 
    $Reply = $ping.send($srv.ServerName) 

    new-object PSObject -Property @{ServerName=$($srv.ServerName); Reply=$($Reply.status)} 

    } 

然后,您可以为不同的组调用脚本:

#groupName A
start-job -FilePath .\forum.ps1 -Name "Test" -ArgumentList "A"
#groupName B
start-job -FilePath .\forum.ps1 -Name "Test" -ArgumentList "B"

Get-Job -name "test" | wait-job | out-null
Get-Job -name "test" | receive-job

#get-job -name "test" |remove-job

如果您使用的是PowerShell V1或sqlps,则可以使用System.Diagnostics.ProcessStartInfo启动单独的powershell.exe进程并传递组名。

param($groupName)

    $StartInfo = new-object System.Diagnostics.ProcessStartInfo
    $StartInfo.FileName = "$pshome\powershell.exe"
    $StartInfo.Arguments = " -NoProfile -Command C:\scripts\forum.ps1 $groupName"
    $StartInfo.WorkingDirectory = "C:\scripts"
    $StartInfo.LoadUserProfile = $true
    $StartInfo.UseShellExecute = $true
    [System.Diagnostics.Process]::Start($StartInfo) > $null

答案 2 :(得分:0)

这是一个page,其中包含一个可能对您有用的脚本。我自己还没有用过它,所以除此之外我不能评论它。

答案 3 :(得分:0)

Powershell根本不会真正做多线程。我已经设法通过伪造它与“start [powershell path] scriptname.ps1”开始的火灾和遗忘脚本将其撬到位。它会触发多个insance,但如果没有通过数据库或其他消息传递机制进行最终运行,则无法从中获取数据。跟踪子进程何时终止也很棘手。

cmd /c "start /min /low C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command .\evtlogparse.ps1  "

在您的情况下,当您将SQL字符串设置为foreach循环的一部分时,您可以尝试将数据库更新代码放入您触发的第二个脚本中。您可能会有许多不同的进程尝试更新同一个数据库表,因此计时问题的可能性非常大。

此外,由于您正在开始使用新的PowerShell实例,因此您需要花费大量内存才能使其正常工作。 foreach循环可能比开始一堆进程更快。

所以,它可以做到,但它并不是什么相似的东西。

答案 4 :(得分:0)

首先,我建议只在'foreach ..'之外创建一次变量$ ping 也许是一个更简单的解决方案......现在您正在使用SQL 2008,为什么不使用SMO方法'enumAvailableSqlServers:“... SMOApplication] :: EnumAvailableSqlServers($ false)”。这将为您提供网络上所有可用服务器的列表。这是Microsoft MSDN链接,因此您可以阅读它: http://msdn.microsoft.com/en-us/library/ms210350.aspx

答案 5 :(得分:0)

如此接近....这就是我所拥有的

add-pssnapin SqlServerCmdletSnapin100

$Server = "ServerName"
$Database = "DatabaseName"

$con = "server=$Server;database=$Database;Integrated Security=sspi"  
$cmd = "SELECT ServerName FROM dbo.vwServerListActive"  

$da = New-Object System.Data.SqlClient.SqlDataAdapter -ArgumentList $cmd, $con 

$dt = New-Object System.Data.DataTable  

$da.Fill($dt) | Out-Null 


foreach ($srv in $dt)  
{     
    Start-Job -ScriptBlock { 
        param ( 
            [String]$Server, 
            [String]$Database, 
            [String]$ServerName 
        ) 


    if (Test-Connection -ComputerName $ServerName -quiet)  
            {  
                $sql = "UPDATE dbo.ServerList SET GoodPing = 1 WHERE GoodPing <> 1 AND ServerName = '$ServerName'" 
            }  
            else  
            {  
                $sql = "UPDATE dbo.ServerList SET GoodPing = 0 WHERE GoodPing <> 0 AND ServerName = '$ServerName'" 
            } 

           Invoke-SqlCmd -ServerInstance $Server -Database $Database -Query $sql 
    } -ArgumentList $Server, $Database, $srv.ServerName 
} 

它看起来正在启动多个工作......但我的表永远不会更新。如果我删除“Start-Job”的东西和争论列表并使用$ srv.ServerName,那么它会像以前一样顺序工作。有任何想法吗? (非常感谢所有回复的BTW)

答案 6 :(得分:0)

以下是来自Jim Truher的脚本,用于PowerShell v1.0中的后台作业:

http://jtruher.spaces.live.com/blog/cns!7143DA6E51A2628D!130.entry

PowerShell v2.0内置了后台作业:

http://technet.microsoft.com/en-us/library/dd347692.aspx

-Oisin