Использование 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. Не используйте старый способ.