长期潜伏者(尤其是在这个问题上)。我一直在研究这个网站上与我的问题直接相关的异步帖子,但我没有找到解决方案。我还尝试了以下代码的许多不同变体,但均无济于事。
发生的事情是我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 内部发生了一些事情导致这种情况发生。