#c# #powershell
#c# #powershell
Вопрос:
Как запустить два командлета параллельно в одном и том же пространстве выполнения. Я использую C #.
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.AuthorizationManager = new AuthorizationManager("MyShellId");
iss.ImportPSModule(new string[] { "MSOnline" });
Runspace powerShellRunspace = RunspaceFactory.CreateRunspace(iss);
В двух потоках я буду запускать командлеты, используя одно и то же пространство выполнения.
Pipeline pipeLine = powerShellRunspace.CreatePipeline();
pipeLine.Commands.Add(shellCommand);
pipeLine.Input.Close();
pipeLine.Invoke();
pipeLine.Output.DataReady = new EventHandler(processData); //processData is a method which processes data emitted by pipeline as and when it comes to avoid out of memory
if (pipeLine.Error != null amp;amp; pipeLine.Error.Count > 0) {
Collection<Object> errors = (Collection<Object>)(pipeLine.Error.ReadToEnd());
//process those errors
}
Но когда два потока одновременно используют одно и то же пространство выполнения для запуска командлетов. Я получаю исключение: «Конвейер не выполнен, потому что конвейер уже выполняется. Конвейеры не могут выполняться одновременно. «
Мне нужно использовать одно и то же пространство выполнения по соображениям производительности. Как достичь моей цели?
Комментарии:
1. В пространствах выполнения отсутствует необходимая синхронизация для поддержки параллелизма. Предполагая, что ваша цель — «хорошая производительность», вам следует подробнее рассказать о том, почему несколько пространств выполнения не дают вам хорошей производительности.
2. Привет @JasonShirk, у меня есть веб-приложение, и поскольку я импортирую модули powershell в runspace, это будет медленно, когда многие пользователи отправят http-запрос. Есть ли у вас какие-либо идеи, как выполнить обработку ошибок в Runspacepool? и я должен привязать этот обработчик событий, чтобы избежать нехватки памяти (обновил свой код)..
3. Я столкнулся с той же проблемой. Кто-нибудь пробовал решения и добился успеха? @Praveen Kumar
4. Привет @ch.smrutiranjanparida, я думаю, что я использовал RunspacePool и назначил этот пул объекту PowerShell и использовал powersellobj. Стримы. Ошибка для обработки ошибок и используется powersellobj.BeginInvoke для передачи ему PSDataCollection для обработки данных, передаваемых командой powershell
Ответ №1:
Вы смотрели на System.Management.Автоматизация.Пространства выполнения.Класс RunspacePool? Использование it и InitialSessionState может помочь устранить накладные расходы при импорте модулей, поскольку это выполняется только один раз для каждого пула, а не для каждого пространства выполнения. Если вы ищете асинхронное выполнение команд powershell, вот очень простой, не готовый к производству пример: (обратите внимание, я не на компьютере с Visual Studio, но это должно быть правильно)
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.AuthorizationManager = new AuthorizationManager("MyShellId");
iss.ImportPSModule(new string[] { "MSOnline" });
#set commands we want to run concurrently
string[] commands = new string[4] {
"Start-Sleep -Seconds 5; 'Hi from #1'",
"Start-Sleep -Seconds 7; 'Hi from #2'",
"Start-Sleep -Seconds 3; 'Hi from #3'",
"throw 'Danger Will Robinson'"
};
Dictionary<PowerShell, IAsyncResult> dict = new Dictionary<PowerShell, IAsyncResult>();
//this loads the InitialStateSession for all instances
//Note you can set the minimum and maximum number of runspaces as well
using(RunspacePool rsp = RunspaceFactory.CreateRunspacePool(iss))
{
rsp.SetMinRunspaces(5);
rsp.SetMaxRunspaces(10);
rsp.Open();
foreach(string cmd in commands)
{
PowerShell ps = PowerShell.Create();
ps.AddScript(cmd);
ps.RunspacePool = rsp;
//Add parameters if needed with ps.AddParameter or ps.AddArgument
dict.Add(ps,ps.BeginInvoke());
}
do{
List<PowerShell> toBeRemoved = new List<PowerShell>();
foreach(KeyValuePair<PowerShell, IAsyncResult> kvp in dict)
{
if(kvp.Value.IsCompleted)
{
try
{
PSDataCollection<PSObject> objs = kvp.Key.EndInvoke(kvp.Value);
foreach(PSObject obj in objs)
{
Console.WriteLine(obj);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
toBeRemoved.Add(kvp.Key);
}
}
}
foreach(PowerShell item in toBeRemoved)
{
dict.Remove(item);
}
//Wait before we check again
Thread.Sleep(200);
} while (dict.Count > 0)
rsp.Close();
}
//Added to keep console open
Console.Read();
Это должно дать:
Hi from #3
Hi from #1
Hi from #2
Комментарии:
1. Привет @StephenP, но как выполнить обработку ошибок при использовании RunspacePool, я обновил свой код, чтобы показать, как я обрабатываю ошибки при использовании одного пространства выполнения. Следует отметить, что у меня есть веб-приложение, поэтому я не могу запускать все команды powershell одновременно асинхронно, команды powershell будут выполняться по мере поступления HTTP-запросов.
2. И перед вызовом команды powershell я должен привязаться
pipeLine.Output.DataReady = new EventHandler(processData);
к обработке данных, передаваемых конвейером по мере необходимости, чтобы избежать нехватки памяти.3. Ошибки, вызванные командами powershell, будут восприниматься как ошибки времени выполнения при выполнении вызова EndInvoke . Вам нужно будет добавить логику обработки ошибок. Вы также можете проверить свойства HadErrors и Streams . Вы все равно можете использовать runspacepool для устранения некоторых накладных расходов при импорте модуля, вам просто нужно реализовать кэширование runspacepool.
4. @StephenP Я не думаю, что вы можете изменять свой словарь во время итерации по нему. toBeRemoved следует добавить в коллекцию элементов, подлежащих удалению, затем каждый элемент в этой коллекции должен быть удален из словаря за пределами вашего цикла foreach (в том же месте, где находится ваш оператор sleep). В противном случае я уверен, что вы получите исключение.
5. @ksun хороший улов. У меня был } в неправильном месте. If(toBeRemoved) должен быть после цикла foreach . У меня было } перед вызовом thread.sleep вместо if(toBeRemoved). Я обновил ответ.