Использование Powershell RunspacePool для многопоточного удаленного сервера из C#

Я пытаюсь создать код C#, используя Powershell RunspacePool для удаленного сервера. Все работает хорошо, когда 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. Эта конфигурация использует стартовый скрипт, который загружает оснастку (Add-PSSnapin 'MySnapin'). Как уже говорилось, это прекрасно работает при использовании максимум 1 пробелов. Но при максимальном количестве 2 рабочих пространств первая задача завершается, следующая выдает ошибку "При запуске сценария запуска возникла ошибка: элемент с таким же ключом уже добавлен". а другая задача не выполнена из-за поврежденного соединения. Кажется, у него есть проблема с загрузкой spanin 2 раза. Поэтому я изменил свой стартовый скрипт на

if ((Get-PSSnapin -Name 'MySnapin' -ErrorAction SilentlyContinue) -eq $null)
{
    Add-PSSnapin 'MySnapin'
}

В этом случае открытие пула уже завершается с ошибкой: при запуске сценария запуска возникла ошибка: не было обнаружено оснасток Windows PowerShell, соответствующих шаблону "MySnapin". Проверьте шаблон и затем повторите команду. Комментируя строку Add-PSSnapin, по-прежнему выдает ошибку, поэтому эта команда выдает команду Get-PSSnapin. Таким образом, похоже, что ErrorAction SilentlyContinue не соблюдается.

Есть идеи?

Второй вопрос: каковы рекомендуемые настройки для pool.ApartmentState и pool. ThreadOptions? Не могу найти документацию по этому вопросу.

ОБНОВЛЕНИЕ:

Я перепробовал все комбинации ApartmentState и ThreadOptions, но это не имело никакого значения (кроме того, что комбинация STA с UseCurrentThread не работает, потому что процесс сервера работает в режиме MTA, это нормально).

Так что, по-видимому, это проблема с командлетами PSSnapin. В обычном процессе Powershell без проблем можно вызывать Add-PSSnapin несколько раз, а командлет Get-PSSnapin соблюдает ErrorAction SilentlyContinue. Кажется, что обе вещи не работают в стартапскрипте. Очень странно.

Но вместо использования Add-PSSnapin я теперь попытался использовать Import-Module для загрузки сборки, и это прекрасно работает.

Чего я сейчас не понимаю, так это того, что изменение параметра maxRunspaces (1, 2 или 3), похоже, не влияет на общую продолжительность. Но это, возможно, связано с тем, что действия на сервере относительно невелики по сравнению с сетевым трафиком и задержкой. Я должен попробовать долгий процесс.

ОБНОВЛЕНИЕ 2:

Хорошо, теперь я протестировал с помощью простой команды Start-Sleep, и я ясно вижу, что многопоточность работает нормально.

Таким образом, конечная проблема, которая все еще существует, в тексте, выделенном жирным курсивом выше. Хотя я могу обойти это с помощью Import-Module

1 ответ

Решение

Первый: -ErrorAction SilentlyContinue применяется только к "не прекращающимся" ошибкам. Завершающие ошибки генерируются, а не записываются, и должны быть зафиксированы, а не подавлены. Пожалуйста, не заводи меня.

Другими словами, заверните его в try { ... } catch { ... } блок вместо использования флага ErrorAction.

Второе: почему вы пытаетесь загрузить его как оснастку? Snapins по сути устарели. В документах MSDN формулировка "Помните, что в Windows PowerShell 2.0 появилась поддержка модулей, что является предпочтительным способом добавления командлетов и поставщиков".

Это мертвая функция, которая не будет расширена и на данный момент полностью заменена модулями. Вы говорите, что он работает с Import-Module. Это твой ответ. Import-Module. Не используйте старый способ.

Другие вопросы по тегам