开发者

Powershell start-job, wait-job, host thread never exits when run from ASP.NET IIS

开发者 https://www.devze.com 2023-03-17 19:00 出处:网络
I am currently trying to build a threaded cleanup script with powershell, initiated from an IIS. I have made a threaded \"Kill process by owner\" using powershell remoting, running from the same list

I am currently trying to build a threaded cleanup script with powershell, initiated from an IIS. I have made a threaded "Kill process by owner" using powershell remoting, running from the same list of servers as my cleanup script, and that works no problem.

$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 

When i run my script from the powershell console, the whole thing runs perfectly, main thread starts 28 new processes, which start a remoting connection to each server, and waits for all the jobs to finish. When they are finished, i get my output and the host thread exists. All running as planned.

Not so when I run it from my asp.net application, i get ""Started Job on: $comp"" for each of my 28 servers, but only a result from the first 14, and then the host thread just sits there. (untill i kill it with fire, and my output is returned to the asp.net webpage)

I have no way to see what happens in the script when i run it from the asp.net page.All i can see is cpu/ram usage drop to the same levels as when i run it from the PSconsole, but my main thread never closes. So I do believe the script works as supposed, but my webpage hangs untill the main thread closes(which it never does, as stated).


This is how I call my script (not the pretty .net <3 powershell way:)

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;
}

The mystery deepens, added this line to my threaded jobs

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

And when i run my script from the IIS, i receive message from each machine. Just as my ram usage shows, all the jobs are run, but the output isnt returned properly, and my host thread just sits there...waiting...


As a note, you've got some unnecessary stuff going on.

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 already constructs a job list; there's no need to do so in a $jobs variable, and no need to enumerate them to do the wait.

Of course, you may use $jobs for something else in your code - but just wanted to make sure other folks see this alternative.


I found the solution. It's a deadlock caused by calling

string output = process.StandardOutput.ReadToEnd();
string errors = process.StandardError.ReadToEnd();

Right after each other. Instead i followed http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandarderror.aspx#Y95 and did this instead:

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;
}

No more hanging :)


you could use something like this to see the commands and the output...

$logfile = "C:\Documents and Settings\username\My Documents\WindowsPowerShell\logs\$(Get-Date -uformat "%Y%m%d - %H%M%S").log"
Start-Transcript -Path $logfile

I'm interested to know how you're calling it from ASP.Net as well?

0

精彩评论

暂无评论...
验证码 换一张
取 消