使用Powershell RunspacePool多线程从C#远程服务器运行

4

我试图使用 Powershell RunspacePool 到远程服务器创建一些 C# 代码。 当 maxRunspaces 设置为 1 时,所有工作都正常进行,但将其设置为 2 会出现问题。

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

在服务器上,我已经使用Register-PSSessionConfiguration注册了一个会话配置,其中shellUri指向该配置。该配置使用一个启动脚本来加载一个snapin(Add-PSSnapin 'MySnapin')。 正如所说的,当使用最多1个runspace时,这个工作正常。但是,如果最大运行空间为2,则第一个任务完成,下一个任务会出现错误“运行启动脚本引发错误:已添加具有相同键的项。”并且其他任务失败,因为连接已损坏。似乎它在两次加载spanin时存在问题。 因此,我将我的启动脚本更改为
if ((Get-PSSnapin -Name 'MySnapin' -ErrorAction SilentlyContinue) -eq $null)
{
    Add-PSSnapin 'MySnapin'
}

在这种情况下,打开池时已经出现错误: 运行启动脚本引发了一个错误:找不到与模式“MySnapin”匹配的Windows PowerShell快照。检查模式,然后再次尝试该命令。 即使注释掉 Add-PSSnapin 行,仍会抛出错误,因此是 Get-PSSnapin 命令引发该错误。所以看起来 ErrorAction SilentlyContinue 没有得到遵守。
有什么想法吗?
第二个问题:pool.ApartmentState 和 pool.ThreadOptions 的推荐设置是什么?找不到相关文档。
更新:
我尝试了所有的 ApartmentState 和 ThreadOptions 组合,但没有任何区别(除了 STA 与 UseCurrentThread 组合无法工作,因为服务器进程在 MTA 模式下运行,这很正常)。
因此显然是 PSSnapin cmdlet 的问题。在普通的 Powershell 进程中,调用 Add-PSSnapin 多次没有问题,Get-PSSnapin cmdlet 也可以遵守 ErrorAction SilentlyContinue。这两件事在启动脚本中似乎都不起作用。非常奇怪。
但是,我现在尝试使用 Import-Module 加载程序集,这个方法很好用。
现在我不明白的是,变化 maxRunspaces 参数(1、2 或 3)似乎不会对总持续时间产生任何影响。但这可能是由于服务器上的操作相对于网络流量和延迟而言相对较小。我应该尝试一个长进程。
更新 2:
好的,现在我使用了一个简单的 Start-Sleep 命令进行了测试,我可以清楚地看到多线程正常工作。
因此,最终存在的问题仍然是上面加粗和倾斜的文字。虽然我可以通过使用 Import-Module 来解决它。

当您在远程计算机上注册PSSessionConfiguration时,是否使用了“-UseSharedProcess”参数?如果是,请尝试在不使用该参数的情况下进行注册。 - Keith Hill
FYI http://connect.microsoft.com/PowerShell/feedback/details/414499/runspacepool-should-support-connectioninfo-and-initialsessionstate-at-the-same-time - Keith Hill
@Keith:不,我没有使用-UseSharedProcess参数。 - Jaap
2
Add-Snapin仅适用于Powershell V1兼容性。它需要Snapin是一个已注册的DLL,并具有注册表设置。在V2及更高版本中,Import-Module是首选方法。模块可以是任何有效的PowerShell代码,不需要注册。 - Eris
1个回答

0

首先:-ErrorAction SilentlyContinue 仅适用于“非终止”错误。终止错误会被抛出,而不是写入,并且必须被捕获,而不是被抑制。请不要让我开始。

换句话说,应该将其包装在 try { ... } catch { ... } 块中,而不是使用 ErrorAction 标志。

其次:为什么你还要尝试将其作为 snapin 加载?Snapin 已经基本上被弃用了。MSDN 文档上的措辞是“请注意,Windows PowerShell 2.0 引入了对模块的支持,这是添加 cmdlet 和提供程序的首选方法。”

它们是一项已死亡的功能,不会再扩展,并已完全被模块所取代。你说它可以使用 Import-Module。那就是你的答案。使用 Import-Module。不要使用旧方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接