Powershell start-job, wait-job,主机线程从ASP运行时永远不会退出.净IIS



我正在尝试用powershell构建一个线程清理脚本,从IIS启动。我已经做了一个线程"杀死进程的所有者"使用powershell远程,从相同的服务器列表作为我的清理脚本运行,这工作没有问题。

$jobs = @()
foreach ($comp in $comps) {
    $jobs += start-job -FilePath ("CleanupJob.ps1") -ArgumentList $comp, $username
    Write-Host "Started Job on: $comp"
}
foreach($job in $jobs)
{
    $job | wait-job | out-null
    receive-job $job.ID
}
Remove-Job -State Completed 

当我从powershell控制台运行我的脚本时,整个事情运行得很好,主线程启动了28个新进程,这些进程启动了与每个服务器的远程连接,并等待所有的工作完成。当它们完成时,我得到我的输出,并且主机线程存在。一切正常。

不是这样的,当我从我的asp.net应用程序运行它时,我得到"开始工作:$comp"为我的28个服务器中的每一个,但只有从前14个结果,然后主机线程只是坐在那里。(直到我用fire杀死它,我的输出返回到asp.net网页)

当我从asp.net页面运行脚本时,我没有办法看到脚本中发生了什么。我所能看到的是cpu/ram使用率下降到与我在PSconsole上运行时相同的水平,但我的主线程从未关闭。所以我相信脚本的工作原理,但我的网页挂起,直到主线程关闭(它从来没有,如前所述)。


这就是我调用脚本的方式(不是漂亮的。net <3 powershell方式:)

public string RunProgramme(string scriptpath, string arguments)
{
    ProcessStartInfo startInfo = new ProcessStartInfo();
    startInfo.FileName = @"powershell.exe";
    startInfo.Arguments = "& '"+scriptpath+"' "+ arguments;
    //return startInfo.Arguments;
    startInfo.RedirectStandardOutput = true;
    startInfo.RedirectStandardError = true;
    startInfo.UseShellExecute = false;
    startInfo.CreateNoWindow = true;
    Process process = new Process();
    process.StartInfo = startInfo;
    process.Start();
    string output = process.StandardOutput.ReadToEnd();
    string errors = process.StandardError.ReadToEnd();
    return output;
}

神秘感加深了,将这行添加到我的线程作业

Invoke-Command -Session $session  -ScriptBlock {param($path) net send mymachine $path}  -Args $msg 

当我从IIS运行我的脚本时,我收到来自每台机器的消息。正如我的ram使用情况所示,所有作业都运行了,但是输出没有正确返回,并且我的主机线程只是坐在那里…等待…

需要注意的是,这里有一些不必要的东西。

foreach ($comp in $comps) {
    start-job -FilePath ("CleanupJob.ps1") -ArgumentList $comp, $username
    Write-Verbose "Started Job on: $comp"
}
Get-Job | Wait-Job | Out-Null
Remove-Job -State Completed 

PowerShell已经构造了一个作业列表;在$jobs变量中不需要这样做,也不需要枚举它们来执行等待。

当然,您可以将$jobs用于代码中的其他内容—但只是想确保其他人看到这个替代。

我找到了解决方案。调用

会导致死锁
string output = process.StandardOutput.ReadToEnd();
string errors = process.StandardError.ReadToEnd();

一个接一个。相反,我遵循http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandarderror.aspx#Y95并这样做:

public string RunProgramme(string scriptpath, string arguments)
{
    ProcessStartInfo startInfo = new ProcessStartInfo();
    startInfo.FileName = @"powershell.exe";
    startInfo.Arguments = "& '"+scriptpath+"' "+ arguments;
    startInfo.ErrorDialog = false;
    startInfo.RedirectStandardOutput = true;
    startInfo.RedirectStandardError = true;
    startInfo.UseShellExecute = false;
    startInfo.CreateNoWindow = true;
    Process process = new Process();
    process.StartInfo = startInfo;
    process.Start();
    process.BeginErrorReadLine(); 
    //Use BeginErrorReadLine on one of the streams to avoid the deadlock 
    //(i didnt need my error stream, but I did need to filter the errors from my output, so i did this)
    string output = process.StandardOutput.ReadToEnd();
    process.WaitForExit(1000 * 60);
    return output;
}

不再挂起

您可以使用这样的代码来查看命令和输出…

$logfile = "C:Documents and SettingsusernameMy DocumentsWindowsPowerShelllogs$(Get-Date -uformat "%Y%m%d - %H%M%S").log"
Start-Transcript -Path $logfile

我很想知道你是如何从ASP调用它的。还有网络?

最新更新