问题
I am trying to create some C# code using a Powershell RunspacePool to a remote server. All is working well when maxRunspaces is set to 1, but setting it to 2 gives troubles.
var connectionInfo = new WSManConnectionInfo(target, shellUri, credential);
using (RunspacePool pool = RunspaceFactory.CreateRunspacePool(1, 2,
connectionInfo))
{
pool.ApartmentState = System.Threading.ApartmentState.STA;
pool.ThreadOptions = PSThreadOptions.UseNewThread;
pool.Open();
var tasks = new List<Task>();
for (var i = 0; i < 12; i++)
{
var taskID = i;
var ps = PowerShell.Create();
ps.RunspacePool = pool;
ps.AddCommand("Get-NAVServerInstance");
var task = Task<PSDataCollection<PSObject>>.Factory.FromAsync(
ps.BeginInvoke(), r => ps.EndInvoke(r));
System.Diagnostics.Debug.WriteLine(
string.Format("Task {0} created", task.Id));
task.ContinueWith(t => System.Diagnostics.Debug.WriteLine(
string.Format("Task {0} completed", t.Id)),
TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t => System.Diagnostics.Debug.WriteLine(
string.Format("Task {0} faulted ({1} {2})", t.Id,
t.Exception.InnerExceptions.Count,
t.Exception.InnerException.Message)),
TaskContinuationOptions.OnlyOnFaulted);
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
On the server I have registered a session configuration with Register-PSSessionConfiguration to which shellUri is pointing. That configuration uses a startupscript which loads a snapin (Add-PSSnapin 'MySnapin'). As said this works fine when using a max of 1 runspaces. But with a max of 2 runspaces the first task completes, the next gives an error "Running startup script threw an error: An item with the same key has already been added." and the other task fail because of a corrupted connection. Seems it has a problem with loading the spanin 2 times. So I changed my startupscript to
if ((Get-PSSnapin -Name 'MySnapin' -ErrorAction SilentlyContinue) -eq $null)
{
Add-PSSnapin 'MySnapin'
}
In that case, opening the pool already fails with the error: Running startup script threw an error: No Windows PowerShell snap-ins matching the pattern 'MySnapin' were found. Check the pattern and then try the command again. Commenting the Add-PSSnapin line, still throws the error, so it is the Get-PSSnapin command throwing that error. So it looks like that the ErrorAction SilentlyContinue is not repected.
Any ideas?
Second question: what are the recommended settings for pool.ApartmentState and pool.ThreadOptions? Can't find must documentation on that.
UPDATE:
I tried all the combinations of ApartmentState and ThreadOptions, but that didn't make any difference (beside that the combination of STA with UseCurrentThread does not work, because the server process is running in MTA mode, that's normal).
So apparently it is a problem with the PSSnapin cmdlets. In a normal Powershell process it is no problem to call Add-PSSnapin multiple times and the ErrorAction SilentlyContinue is respected by the Get-PSSnapin cmdlet. Both things seem not to work in a startupscript. Very strange.
But instead of using Add-PSSnapin, I now tried using Import-Module to load the assembly and that works fine.
What I don't understand now is that varying the maxRunspaces parameter (1, 2 or 3) seems not to make any difference in the total duration. But that's perhaps caused by the fact that the actions on the server are relatively small compared to the network traffic and latency. I should try a long process.
UPDATE 2:
Ok, I have now tested with a simple Start-Sleep command and I can clearly see that multithreading is working fine.
So the ultimate problem still existing is in the bold-italic text above. Although I can workaround it by using Import-Module
回答1:
First: -ErrorAction SilentlyContinue
only applies to "non-terminating" errors. Terminating errors are thrown, not written, and must be caught, not suppressed. Please don't get me started.
In other words, wrap it in a try { ... } catch { ... }
block instead of using the ErrorAction flag.
Second: Why are you trying to load it as a snapin anyway? Snapins are essentially deprecated. Te wording on the MSDN docs is "Be aware that Windows PowerShell 2.0 introduced support for modules, which is the preferred method to add cmdlets and providers."
They're a dead feature which won't be expanded and is fully replaced by modules at this point. You say that it works with Import-Module. That's your answer. Import-Module. Don't use the old way.
来源:https://stackoverflow.com/questions/18382401/using-powershell-runspacepool-multithreaded-to-remote-server-from-c-sharp