0

I have a Winforms app that lives in the taskbar area. A window opens up for logging output.

Now, in my component (still on UI Thread here) I need to call an external process that runs 5-15min and produces files. I need to wait for the process to exit and consume those files.

Since I want my UI to be responsive (move the Window, etc.) I implemented an agent and am calling the process with 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;
}

In the calling code I am polling the IAsyncResult with WaitOne() but notice that the UI is extremely unresponsive if not frozen:

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 tells me the external process is started on a worker thread, but why does that not help with my UI responsiveness? IT should NOT block the calling thread

Here's the code that launches the process:

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 );

Hint: To test, the external app.exe is a dummy app that does nothing else but Thread.Sleep(60000). CPU Load is 3%.

Another question: How would I do this the "TPL way" without using Begin/EndInvoke?

4

2 に答える 2

1

Try executing this in your Main thread:

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();
于 2013-03-14T23:16:57.383 に答える
0

Your code starts a method asynchronously but then blocks in a busy waiting loop checking the IsComplete flag. That's not the proper way to use delegates.

Instead of busy-waiting, you should pass a callback function to BeginInvoke that will be called when the delegate finishes. This is already covered in the docs. Check "Executing a Callback method when an asynchronous call completes" in MSDN's "Calling Synchronous Methods Asynchronously".

In your case you would have to write:

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

where MyMethod is the method that will process the results of the call.

于 2013-03-15T11:54:23.353 に答える