1

长期潜伏者(尤其是在这个问题上)。我一直在研究这个网站上与我的问题直接相关的异步帖子,但我没有找到解决方案。我还尝试了以下代码的许多不同变体,但均无济于事。

发生的事情是我ps.BeginInvoke()在加载命令后异步执行,使用某种方法异步启动调用,它确实总是异步运行!但问题是(例如),执行的命令有时只会返回结果 10 次中的 2 次,有时 10 次中的 8 次。这是我正在执行的当前代码的示例:

internal class aSyncPowershellCommand
{
    //private static Collection<Collection<PSObject>> GlobalResults = new Collection<Collection<PSObject>>();

    // This exists simply
    public bool Finished { get; set; }

    private object instanceLock = new object();
    private PowerShell ps;
   // private Guid uniqueGuid = Guid.NewGuid();

    private PSDataCollection<PSObject> psresult;

    public async Task<bool> PowershellLaunchPool(Dictionary<ARS_GroupObject, string> itemListing)
    // itemListing contains a custom class that specified details about an ARS group object
    // and the String "Value" contains the action (in this case, "get-QADGroup".

    {
        // Create a common RunspacePool --- it should be noted that for testing purposes, I am sending exactly 8 groups as 
        // part of the query, for testing purposes.
        RunspacePool pool = RunspaceFactory.CreateRunspacePool(1, 8);

        pool.CleanupInterval = new TimeSpan(0, 5, 0);
        pool.Open();

        // Create a spot to track the results in a unique way (I have experimented with lists, dictionaries, etc.... the results are
        // always the same though, this just happens to be my latest method to attempt to avoid the "Key already added" error"

        ConcurrentDictionary<Guid, IAsyncResult> powershellCommandResults = new ConcurrentDictionary<Guid, IAsyncResult>();
        ConcurrentDictionary<Guid, PowerShell> powershellSessions = new ConcurrentDictionary<Guid, PowerShell>();

        // Locking the instance, again, not sure if this actually does anything as far as my code is concerned, but it's
        // still keeping in line with the same results as before.... semi-success and a lot of "key already added" errors.
        lock (instanceLock)
        {
        foreach (var grpPair in itemListing)
        {
            // Create a unique GUID for the dictionary, just in case this is what was triggering the "key already added" error
            Guid uniqueGuid = Guid.NewGuid();

            //  This passes text output to a textbox, not important
            StatusUpdater.PassPowershellStatus(uniqueGuid.ToString() + " - 1\n");

            // Create a fresh PowerShell instance
            this.ps = PowerShell.Create();
            //StatusUpdater.PassPowershellStatus(this.ps.InstanceId.ToString());

            // Add this fresh PowerShell instance to the common RunspacePool
            this.ps.RunspacePool = pool;

            // Create a fresh instance of psresult to keep track of changes.  This is honestly one piece of code I am suspicious
            // of as far as throwing the "key already added" error, but I haven't been able to isolate.
            this.psresult = new PSDataCollection<PSObject>();

            // grpPair.value is "get-QADGroup"
            this.ps.Commands.AddCommand(grpPair.Value);
            //grpPair.Key.Groupname ---- name of the group
            this.ps.Commands.AddParameter("identity", grpPair.Key.GroupName);

            // loading up the collection of PowerShell sessions to keep track of, trying as hard as possible to keep this
            // all as unique as I possibly can.
            powershellSessions.TryAdd(uniqueGuid, this.ps);

            // Reference event handlers for when something happens with the variable psResult
            this.psresult.DataAdded += new EventHandler<DataAddedEventArgs>(tmpInstance_DataAdded);
            this.ps.InvocationStateChanged += new EventHandler<PSInvocationStateChangedEventArgs>(ps_InvocationStateChanged);
        }       

        }

        foreach (var execute in powershellSessions)
        {
            // Looping through all of the PowerSHell sessions queue up and invoking, relying on their same instance of psresult 
            // to return the results and trigger the tmpInstance_DataAdded and ps_InvocationStateChanged events.
            powershellCommandResults.TryAdd(execute.Key, execute.Value.BeginInvoke<PSObject, PSObject>(null, this.psresult));
        }

        while (Finished != true)
        {
            Thread.Sleep(3000);
        }
        return true;
    }

    private void tmpInstance_DataAdded(object sender, DataAddedEventArgs e)
    {
        PSDataCollection<PSObject> tmpReturn = (PSDataCollection<PSObject>)sender;
        foreach (var item in tmpReturn)
        {
            StatusUpdater.PassPowershellStatus(item.BaseObject.ToString() + "\n");
        }
    }

    private void ps_InvocationStateChanged(object sender, PSInvocationStateChangedEventArgs e)
    {
        var psInfo = (PowerShell)sender;

        if (e.InvocationStateInfo.State == PSInvocationState.Completed)
        {
            StatusUpdater.PassPowershellStatus("RESULT --- " + e.InvocationStateInfo.Reason.ToString() + "\n");
            psInfo.Stop();
            psInfo.Dispose();
            psInfo = null;

        }
        if (e.InvocationStateInfo.State == PSInvocationState.Failed)
        {
            StatusUpdater.PassPowershellStatus("*** ERROR - \n" + e.InvocationStateInfo.Reason.ToString() + " ***\n");
            psInfo.Stop();
            psInfo.Dispose();
            psInfo = null;
          //  StatusUpdater.PassPowershellStatus(e.InvocationStateInfo.Reason.ToString());
        }

    }
}

我已经多次尝试不同的组合IASyncResult result = ps.BeginInvoke()并等待该命令,但它的结果与上面的代码相同。还尝试将其放入var task = Task.Factory.FromAsync(ps.beginInvoke(), p => ps.EndInvoke(p));样式块并添加到任务列表中,然后再次等待它们的所有执行.ConfigureAwait(false);......结果相同!

那么结果如何呢?命令全部执行,但在尝试不成功时,将引发以下异常:

System.Management.Automation.CmdletInvocationException: An item with the same key has already been added. 
---> System.ArgumentException: An item with the same key has already been added.

否则,它会很快返回结果。

我认为问题在于 PowerShell 会话正在尝试写入相同的内部输出对象,尽管它正在执行为

1.) 它是自己的新 PowerShell 实例
2.) 在由 RunSpace 池管理的不同运行空间中。

每次运行脚本时,此错误至少发生一次。有时它实际上会吐出 10 个组结果中的 8 个,有时它只对 1 个有效。

我怎样才能阻止这种碰撞的发生?我怀疑 PowerShell 内部发生了一些事情导致这种情况发生。

4

0 回答 0