How to make C# Powershell Invoke member thread safe
I have this service that, when request is received, runs a powershell command and returns result. Here is the invoker class code:
public class PowerShellScript {
public PowerS开发者_Python百科hellScript() {
}
public Object[] Invoke( String strScriptName, NameValueCollection nvcParams ) {
Boolean bResult = true;
int n = 0;
Object[] objResult = null;
PowerShell ps = PowerShell.Create();
String strScript = strScriptName;
for (n = 0; n < nvcParams.Count; n++) {
strScript += String.Format( " -{0} {1}", nvcParams.GetKey( n ), nvcParams[n] );
}
//ps.AddScript( @"E:\snapins\Init-profile.ps1" );
ps.AddScript( strScript );
Collection<PSObject> colpsOutput = ps.Invoke();
if (colpsOutput.Count > 0)
objResult = new Object[colpsOutput.Count];
n = 0;
foreach (PSObject psOutput in colpsOutput) {
if (psOutput != null) {
try {
objResult[n] = psOutput.BaseObject;
}
catch (Exception ex) {
//exception should be handeled properly in powershell script
}
}
n++;
}
colpsOutput.Clear();
ps.Dispose();
return objResult;
}
}
Method Invoke returns all results returned by powershell script.
All fine and well. As long as this runs in a single thread. As some powershell scripts we invoke can take up to an hour to complete and we don't want for service to do nothing in that time, we decided to go multi-threaded. Unfortunately Powershell class is not thread safe, resulting in sever memory leaks and cpu burn rate. However, if I use lock on Invoke method, this would mean that the entire idea why we went multithreaded will go down the drain.
Any ideas how to solve this?
You can use BeginInvoke()
method of PowerShell
class instead of Invoke()
that you use. In this case you execute your script asynchronously and do not block the calling thread. But you have to review your whole scenario as well. Your old synchronous method returns results that can be easily consumed right after the call. In new asynchronous approach this is not possible in the same way.
see http://msdn.microsoft.com/en-us/library/system.management.automation.powershell.begininvoke
Anyway... I gave up on multithreading when executing powershell commands. I created a small program that is able to execute powershell scripts. Then, each thread creates new process for that program. I know it is a bit of an overhead, but it works.
Basically, Powershell classes are not thread safe. Except static variables (http://msdn.microsoft.com/en-us/library/system.management.automation.powershell%28VS.85%29.aspx).
Hence, an attempt to call multiple scripts via separate threads results in memory leakage and some unexplained CPU usage. My wild guess is that it doesn't close properly. Changing from multi-threaded to multi-process environment should sort things out. However, that means a major politics change.
精彩评论