Despertar 提供的答案将起作用,但可以稍微改进。
使用 Thread.Sleep 在循环中轮询应替换为使用 AutoResetEvent。这将导致主线程仅在有实际可用数据时“唤醒”,并且可以让 cmdlet 以超过 100 毫秒的速度完成。Thread.Sleep 总是会导致 cmdlet 至少花费 100 毫秒,即使它可以运行得更快。如果您有一个简单的 cmdlet,这可能不是问题,但如果您将其插入到复杂的管道中,这 100 毫秒很容易成倍增加并导致运行速度非常慢。此外,在 Listen 方法内访问主线程上的队列时,应采取锁定。
故事的寓意:如果你做跨线程同步 Thread.Sleep 不是正确的工具。
using System.Threading;
public class PowerShellAdapter
{
private Cmdlet Cmdlet { get; set; }
private Queue<object> Queue { get; set; }
AutoResetEvent sync;
private object LockToken { get; set; }
// volatile, since it will be written/read from different threads.
volatile bool finished;
public bool Finished
{
get { return finished; }
set
{
this.finished = value;
// allow the main thread to exit the outer loop.
sync.Set();
}
}
public int Total { get; set; }
public int Count { get; set; }
public PowerShellAdapter(Cmdlet cmdlet, int total)
{
this.Cmdlet = cmdlet;
this.LockToken = new object();
this.Queue = new Queue<object>();
this.finished = false;
this.Total = total;
this.sync = new AutoResetEvent(false);
}
public void Listen()
{
ProgressRecord progress = new ProgressRecord(1, "Counting to 100", " ");
while (!Finished)
{
while (true) { // loop until we drain the queue
object item;
lock (LockToken) {
if (Queue.Count == 0)
break; // exit while
item = Queue.Dequeue();
}
progress.PercentComplete = ++Count * 100 / Total;
progress.StatusDescription = Count + "/" + Total;
Cmdlet.WriteObject(item);
Cmdlet.WriteProgress(progress);
}
sync.WaitOne();// wait for more data to become available
}
}
public void WriteObject(object obj)
{
lock (LockToken)
{
Queue.Enqueue(obj);
}
sync.Set(); // alert that data is available
}
}
请注意,我还没有实际测试过这段代码,但它说明了这个想法。