Search code examples
c#.netasynchronousevent-handling.net-4.0

Best practice to handle async events and run huge post-processing code


Async event handler

I have this class to run an executable asynchronously:


namespace Feather
{
    internal class Helper
    {
        private static Process cmd;

        public static void RunLogic(string args)
        {
            cmd = new Process();

            cmd.StartInfo.FileName = "Cotton.exe";
            cmd.StartInfo.Arguments = args;
            cmd.EnableRaisingEvents = true;
            cmd.Exited += new EventHandler(cmd_Exited);
        }

        private static void cmd_Exited(object sender, EventArgs e)
        {
            // Process finished.
            cmd.Dispose();

            // An output file is generated by the executable.
            // The output file needs post-processing.
            // Post-processing is a huge code base.
            // TODO: Best practice to run huge post-processing code here?
        }
    }
}

Trigger

I trigger exe to run from within another class like this:


namespace Feather
{
    public class FeatherHollow : Command
    {

        protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            string args = "";

            // Trigger async run of the executable.
            Helper.RunLogic(args);

            // TODO: How to run the huge post-processing code when the output file
            // is generated by the executable.

            return Result.Success;
        }
    }
}

Question

What is the best practice and simplest way to run a huge post-processing code when the async process finishes?

delegate approach

Is this the best way?


namespace Feather
{
    internal class Helper
    {
        public delegate void PostProcess(object sender, EventArgs e);

        public static void RunLogic(string args, PostProcess pp)
        {
                // ...
                cmd.Exited += new EventHandler(cmd_Exited);
                cmd.Exited += new EventHandler(pp);
                // ...
        }
    }
}

And caller class:


namespace Feather
{
    public class FeatherHollow : Command
    {
        protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            // ...
            Helper.RunLogic(args, PostProcess);
            // ...
        }

        private static void PostProcess(object sender, EventArgs e)
        {
            // Huge post-processing code is here.
        }
    }
}

Solution

  • I would suggest using Task and async await:

    public static Task RunLogic(string args, CancellationToken cancel = default)
    {
        cmd = new Process();
        // ...
    
       return cmd.WaitForExitAsync(cancel);
    }
    

    This lets you use await when calling the method, and that tend to make the code simpler to read than using delegates/callbacks.

    await RunLogic(...);
    // do post process stuff
    

    If you want to return a value from RunLogic:

    public static async Task<string> RunLogic(string args, CancellationToken cancel = default)
    {
        cmd = new Process();
        // ...
    
       await cmd.WaitForExitAsync(cancel);
       
       return myResultValue;
    }
    

    If you are using an older version of .Net, you can replicate the same functionality using TaskCompletionSource.