Msdn文档说WriteVerbose方法:
“只能从BeginProcessing,ProcessRecord和EndProcessing方法的实现中调用此方法,并且只能从该线程调用。如果此调用是从这些实现外部或从另一个线程调用,则抛出InvalidOperationException异常。”
但在我的测试代码中,我能够在三种标准方法之外调用该方法。但是当从另一个线程调用该方法时,我得到一个例外。
我相信这里的大多数人在实现自定义cmdlet时都需要多线程支持。
我正在使用后台工作程序来完成简单的多线程。我在ProcessRecord函数中等待doWork事件在我继续之前完成。现在我想使用doWork的writeverbose向用户报告进度,但由于它是一个不同的线程,我无法做到。
我尝试使用带有AutoResetEvent的并发队列来实现这一点,但是有更好的方法可以更简单的方式解决这个问题吗?
答案 0 :(得分:4)
如果您使用的是BackgroundWorker
,请订阅其ProgressChanged
个活动。在DoWork()
内,您可以使用ReportProgress()
来发送ProgressChanged
个事件。在cmdlet上,ProgressChanged处理程序可以使用进度信息执行WriteVerbose调用。但是,由于您没有调用任何表单,因此必须手动设置SynchronizationContext
以使BackgroundWorker在您的运行空间线程上执行事件回调。在创建BackgroundWorker之前,请进行以下调用:
System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
使WindowsFormsSynchronizationContext起作用的技巧是AFAICT,你必须抽取消息。以下是我的有限测试:
using System.ComponentModel;
using System.Diagnostics;
using System.Management.Automation;
using System.Threading;
using System.Windows.Forms;
namespace CmdletExp
{
[Cmdlet("Invoke", "LongRunning")]
public class InvokeLongRunningCommand : PSCmdlet
{
private readonly BackgroundWorker _backgroundWorker;
private readonly AutoResetEvent _autoResetEvent;
public InvokeLongRunningCommand()
{
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
_backgroundWorker.ProgressChanged += _backgroundWorker_ProgressChanged;
_autoResetEvent = new AutoResetEvent(false);
}
protected override void EndProcessing()
{
Debug.WriteLine("EndProcessing ThreadId: " + Thread.CurrentThread.ManagedThreadId);
_backgroundWorker.RunWorkerAsync();
do
{
Application.DoEvents();
} while (!_autoResetEvent.WaitOne(250));
Application.DoEvents();
base.EndProcessing();
}
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Debug.WriteLine("DoWork ThreadId: " + Thread.CurrentThread.ManagedThreadId);
for (int i = 0; i < 20; i++)
{
_backgroundWorker.ReportProgress(i * 5);
Thread.Sleep(1000);
}
_autoResetEvent.Set();
}
void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Debug.WriteLine("ProgressChanged ThreadId: " + Thread.CurrentThread.ManagedThreadId + " progress: " + e.ProgressPercentage);
WriteVerbose("Progress is " + e.ProgressPercentage);
}
}
}
像这样调用:
PS> Invoke-LongRunning -Verbose
VERBOSE: Progress is 0
VERBOSE: Progress is 5
VERBOSE: Progress is 10
VERBOSE: Progress is 15
VERBOSE: Progress is 20
VERBOSE: Progress is 25
...
答案 1 :(得分:-1)
我只是要求我使用的所有方法都使用Cmdlet,就像这样;
public static bool bpNewLDSInstance(**Cmdlet ni**, string Computername, string InstanceName, installType InstallType, int LDAPPort, int SSLPort, string dbPath, string logPath,
string ImportLDIFFiles, string Partition, string SourceServer, int SourcePort, string ServiceAccount, SecureString ServicePassword, string Administrator, ADLDSInstance InstIn)
{
bool bInstanceValidated = false;
ni.WriteVerbose("1");
}
然后当我从我的Cmdlet调用它时,我只是将cmdlet传递给了,
bool bSuccess = Program.bpNewLDSInstance(**this**, Computername, InstanceName, InstallType, LDAPPort, SSLPort, dbPath, logPath,
ImportLDIFFiles, Partition, SourceServer, SourcePort, ServiceAccount, ServicePassword, Administrator, InstIn);