Как я могу собрать данные возвращаемого значения при использовании пула пространства выполнения и begininvoke?

У меня этот код хорошо работает синхронно с powershell.Invoke(), однако с powershell.BeginInvoke() я не могу зафиксировать вывод. Чтобы использовать приведенный ниже код, вам нужно заполнить переменную $servers, иначе она должна работать.

Я попытался зафиксировать вывод, добавив каждый поток в переменную $threads при использовании EndInvoke(), и я могу видеть дескриптор потока и значение iscompleted, однако я не могу понять, где значение, которое я возвращаю с возвратом сохраняется часть каждой функции.

Первый блок - это вывод, который я вижу, показывая false для одного асинхронного завершения, пока он не завершится, а затем все дескрипторы потоков показывают true.

Спасибо!

8804 is True
16420 is True
13352 is True
11184 is True
3872 is True
8288 is True
17296 is False
20816 is True
11628 is True
17688 is True
12856 is True
19400 is True

8804 is True
16420 is True
13352 is True
11184 is True
3872 is True
8288 is True
17296 is True
20816 is True
11628 is True
17688 is True
12856 is True
19400 is True


Thread count: 12
Time elapsed: 3


cls;

$stopwatch =  [system.diagnostics.stopwatch]::StartNew();

#region Runspace Pool

[runspacefactory]::CreateRunspacePool() | Out-Null;
$SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault();
$RunspacePool = [runspacefactory]::CreateRunspacePool(

    1, #Min Runspaces

    16 #Max Runspaces

);
$RunspacePool.Open();

#endregion Runspace pool

$threads = New-Object System.Collections.ArrayList;
$servers = @("goodServer1", "goodServer2", "badServer1", "goodServer3");

foreach ($server in $servers)
{
    $PowerShell = [powershell]::Create();
    $PowerShell.RunspacePool = $RunspacePool;

    [void]$PowerShell.AddScript({

        Param ($server, $portNumber)
        
        [pscustomobject]@{

            server = $server
            portNumber = $portNumber

        } | Out-Null
    
        Function testPort ($server, $portNumber)
        {
            $testPort = New-Object System.Net.Sockets.TCPClient # -ArgumentList $server, 3389;
            $testPort.SendTimeout = 3;
            try
            {
                $testPort.Connect($server, $portNumber);
            }
            catch
            {
                #do nothing;
            }
            $result = $testPort.Connected;
            $testPort.Close();          
            $dateTime = ([DateTime]::Now.ToString());
            
            return "$server|testPort|$result|$dateTime"; # server | function | result | DateTime
        }


        testPort -server $server -portNumber $portNumber;
    }) # end of add script

    $portNumber = "3389";
    $PowerShell.AddParameter('server', $server).AddParameter('portNumber', $portNumber) | Out-Null;
    $returnVal = $PowerShell.BeginInvoke();
    $temp = "" | Select PowerShell,returnVal;
    $temp.PowerShell = $PowerShell;
    $temp.returnVal = $returnVal;
    $threads.Add($Temp) | Out-Null;
    
    
    $PowerShell = [powershell]::Create();
    $PowerShell.RunspacePool = $RunspacePool;
    [void]$PowerShell.AddScript({

        Param ($server, $shareName, $timeOutInMs)
        
        [pscustomobject]@{

            server = $server
            shareName = $shareName
            timeOutInMs = $timeOutInMs

        } | Out-Null
    

        Function testShare ($server, $shareName, $timeOutInMs)
        {
        $cSharp = 
@'
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace cSharp7
{
    public class cSharpClass
    {
        public bool verifyDirectoryExists(string uri, int timeoutInMs)
        {
            var task = new Task<bool>(() =>
            {
                var dir = new DirectoryInfo(uri);
                return dir.Exists;
            });
            task.Start();
            return task.Wait(timeoutInMs) && task.Result;
        }
        
        public bool verifyFileExists(string uri, int timeoutInMs)
        {
            var task = new Task<bool>(() =>
            {
                var fi = new FileInfo(uri);
                return fi.Exists;
            });
            task.Start();
            return task.Wait(timeoutInMs) && task.Result;
        }
    }
}
'@

            $assemblies = ("System", "System.Collections", "System.ComponentModel", "System.Data", "System.Drawing", "System.Linq", "System.Threading.Tasks", "System.Windows.Forms", "System.Management.Automation", "System.Security", "System.Threading", "System.Collections.Concurrent", "System.Security.Principal", "System.Management", "System.IO", "System.Collections");
            Add-Type -TypeDefinition $cSharp -ReferencedAssemblies $assemblies -Language CSharp;
            $directoryExists = New-Object CSharp7.cSharpClass;
            $path = "\\" + $server + "\" + $shareName;
            try
            {
                $result = $directoryExists.verifyDirectoryExists($path, $timeOutInMs); # has a 2 minute timeout period, needs an asynchronous thread with a timeout period
                #Write-Host $result;
            }
            catch
            {
                # do nothing
            }
            
            $dateTime = ([DateTime]::Now.ToString());
            return "$server|testShare|$result|$dateTime"; # server | function | result | DateTime
        }

        testShare -server $server -shareName $shareName -timeOutInMs $timeOutInMs;
    }) # end of add script

    $shareName = "c$";
    $timeOutInMs = "3000";
    $PowerShell.AddParameter('server', $server).AddParameter('shareName', $shareName).AddParameter('timeOutInMs', $timeOutInMs) | Out-Null;
    $returnVal = $PowerShell.BeginInvoke();
    $temp = "" | Select PowerShell,returnVal;
    $temp.PowerShell = $PowerShell;
    $temp.returnVal = $returnVal;
    $threads.Add($Temp) | Out-Null;
    
    
    $PowerShell = [powershell]::Create();
    $PowerShell.RunspacePool = $RunspacePool;
    [void]$PowerShell.AddScript({

        Param ($server, $pingCount)
        
        [pscustomobject]@{

            server = $server
            pingCount = $pingCount

        } | Out-Null
    

        Function testPing ($server, $pingCount)
        { 
            try
            {
                $result = Test-Connection $server -Count $pingCount -Quiet;
            }
            catch
            {
                # do nothing
            }
            
            $dateTime = ([DateTime]::Now.ToString());
            
            return "$server|testPing|$result|$dateTime"; # server | function | result | DateTime
        } 
        
        testPing -server $server -pingCount $pingCount;
    }) # end of add script

    $pingCount = "1";
    $PowerShell.AddParameter('server', $server).AddParameter('pingCount', $pingCount) | Out-Null;
    $returnVal = $PowerShell.BeginInvoke();
    $temp = "" | Select PowerShell,returnVal;
    $temp.PowerShell = $PowerShell;
    $temp.returnVal = $returnVal;
    $threads.Add($Temp) | Out-Null;
}


$completed = $false;
while ($completed -eq $false)
{
    $completed = $true;
    
    foreach ($thread in $threads)
    {
        $endInvoke = $thread.PowerShell.EndInvoke($thread.returnVal);
        $endInvoke;
        $threadHandle = $thread.returnVal.AsyncWaitHandle.Handle;
        $threadIsCompleted = $thread.returnVal.IsCompleted;
        #Write-Host "$threadHandle is $threadIsCompleted";
        if ($threadIsCompleted -eq $false)
        {
            $completed = $false;
        }
    }
    Write-Host "";
    sleep -Milliseconds 500;
}

foreach ($thread in $threads)
{
    $thread.PowerShell.Dispose();
}

$stopwatch.Stop();

Write-Host "";
Write-Host "Thread count:" $threads.Count;
Write-Host "Time elapsed:" $stopwatch.Elapsed.Seconds;

person Bbb    schedule 30.04.2021    source источник
comment
Каков ваш ожидаемый результат? Как должен выглядеть возвращаемый объект? Глядя на ваш код, действительно сложно сказать, почему бы для функции C# не использовать Test-Path?   -  person Santiago Squarzon    schedule 30.04.2021


Ответы (1)


Вот как вы собираете данные о возвращаемом значении. Вы определяете пользовательский объект $temp с двумя именами свойств, в данном случае Powershell и returnVal. Затем вы добавляете их в список массивов. После завершения асинхронного BeginInvoke вы можете вызвать EndInvoke для asyncResult, выполнив следующее: $endInvoke = $thread.PowerShell.EndInvoke($thread.returnVal); и разбирай как хочешь.

Это должен быть самый сложный сценарий, который я когда-либо писал, с пулом пространства выполнения, пространствами выполнения, асинхронным возвратом, передаваемыми функциями и даже примесью C#. Надеюсь, другие смогут использовать части или все это.

$returnVal = $PowerShell.BeginInvoke();
$temp = "" | Select PowerShell,returnVal;
$temp.PowerShell = $PowerShell;
$temp.returnVal = $returnVal;
$threads.Add($Temp) | Out-Null;

$completed = $false;
while ($completed -eq $false)
{
    $completed = $true;
    
    foreach ($thread in $threads)
    {
        $endInvoke = $thread.PowerShell.EndInvoke($thread.returnVal);
        $endInvoke;
        $threadHandle = $thread.returnVal.AsyncWaitHandle.Handle;
        $threadIsCompleted = $thread.returnVal.IsCompleted;
        #Write-Host "$threadHandle is $threadIsCompleted";
        if ($threadIsCompleted -eq $false)
        {
            $completed = $false;
        }
    }
    Write-Host "";
    sleep -Milliseconds 500;
}

foreach ($thread in $threads)
{
    $thread.PowerShell.Dispose();
}
person Bbb    schedule 30.04.2021