Могут ли конвейеры выполняться одновременно в одном и том же пространстве выполнения?

#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). Я обновил ответ.