在C#/ WinForms中调用外部进程会使UI无响应。为什么?

时间:2013-03-14 21:46:05

标签: c# .net asynchronous task-parallel-library

我有一个Winforms应用程序,它位于任务栏区域。将打开一个窗口以记录输出。

现在,在我的组件中(仍然在UI线程上)我需要调用一个运行5-15分钟并生成文件的外部进程。我需要等待进程退出并使用这些文件。

由于我希望我的UI能够响应(移动Window等),我实现了一个代理,并使用BeginInvoke / EndInvoke调用该进程:

private delegate int BeginCallDelegate( int port, int baud, int timeout, Job job );
private BeginCallDelegate del = null;

public IAsyncResult BeginCall( int port, int baud, int timeout, Job job )
{
    del = new BeginCallDelegate( Call );
    IAsyncResult iar = del.BeginInvoke( port, baud, timeout, job, null, null );
    return iar;
}

在调用代码中,我使用WaitOne()轮询IAsyncResult,但是如果不是冻结,请注意UI非常无响应

IAsyncResult a = agent.BeginCall(...); //BeginInvoke
while ( !a.IsCompleted )
{
    iar.AsyncWaitHandle.WaitOne( 250 );
    //writing something to a textbox works here, but overall responsiveness is weak
}

agent.EndCall( iar ); //EndInvoke

VS告诉我外部进程是在工作线程上启动的,但为什么这对我的UI响应无效?它不应该阻止调用线程

以下是启动流程的代码:

ProcessStartInfo psi = new ProcessStartInfo();

psi.FileName = "app.exe";
psi.Arguments = String.Format( "blah blah", port, baud, timeout, job.FullName );

psi.CreateNoWindow = false;
psi.ErrorDialog = false;
psi.WindowStyle = ProcessWindowStyle.Hidden;

process.StartInfo = psi;

if ( !process.Start() )
    throw new Exception( "The process cannot start." );

process.WaitForExit( job.Timeout );

提示:要测试,外部app.exe是一个虚拟应用程序,除了Thread.Sleep(60000)之外别无其他功能。 CPU负载为3%。

另一个问题:如何在不使用Begin / EndInvoke的情况下执行“TPL方式”?

2 个答案:

答案 0 :(得分:1)

尝试在主线程中执行此操作:

ProcessStartInfo psi = new ProcessStartInfo();
Thread processStarter = new Thread(delegate()
{
psi.FileName = "app.exe";
psi.Arguments = String.Format( "blah blah", port, baud, timeout, job.FullName );
psi.CreateNoWindow = false;
psi.ErrorDialog = false;
psi.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo = psi;
process.Start();
process.WaitForExit( job.Timeout );
//call methods that deal with the files here
});
processStarter.IsBackground = true;
processStarter.Start();

答案 1 :(得分:0)

您的代码以异步方式启动方法,但随后在忙等待循环中阻塞,检查IsComplete标志。这不是使用代表的正确方法。

您应该将回调函数传递给将在委托完成时调用的BeginInvoke,而不是忙等待。这已在文档中介绍。在MSDN的"Calling Synchronous Methods Asynchronously"中检查“在异步调用完成时执行回调方法”

在你的情况下,你必须写:

del.BeginInvoke( port, baud, timeout, job, new AsyncCallBack(MyMethod), null );

其中MyMethod是处理调用结果的方法。