Home > OS >  How can I use async in C# to get the result I need from a python script called as a process?
How can I use async in C# to get the result I need from a python script called as a process?

Time:06-30

I have built a GUI using C# .NET and I need to incorporate a Python script to be called if a checkbox is selected on the panel when the "PROCEED" button is clicked. The python script should run in the background and then I'd like to print the string result in a message box at the end of the main process launched by the GUI. The important thing is that I only want the message box to pop up if the checkbox was selected.

I understand how I can call Python from C#, but I am still a bit fuzzy on how I can collect and display the result using an async function. I understand based on Microsoft docs that I need to use the Task object, but I cannot quite get this to function the way I am intending. Here are the docs I used in my code so far for reference: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

This is my first time using async in C#.

Here is my code for the relevant functions(shortened for clarity), and then I will describe the issues in detail with questions:

async private void proceedClicked()
{    
    if (checkBox.Checked)
    {
        string cmd = ""; // some python command
        Task<string> pythonTask = runProcAsync(cmd);
    }

    // do some other processing

    if (checkBox.Checked)
    {
        var pythonResult = await pythonTask;
        // print result to a message box
    }
}

private Task runProcAsync(string cmd)
{
    return Task.Run(() =>
        {
            callPython(cmd);
        });
}
    
private string callPython(string cmd)
{
    ProcessStartInfo start = new ProcessStartInfo();
    start.FileName = "python.exe";// full path to python
    start.Arguments = cmd;
    start.UseShellExecute = false;
    start.RedirectStandardOutput = true;
    using (Process process = Process.Start(start))
    {
        using (StreamReader reader = process.StandardOutput)
        {
            string result = reader.ReadToEnd();
            return result;
        }
    }
}

Here are my issues with some details about my implementation approaches:

  1. How do I align the Task and Task objects so that they match? I have tried using Task and then Task.Result() to get the string, but Result() doesn't exist as a method for the Task var I declared. Then I have tried using Task<> as the type, but Task.Run() complains about the implicit conversion as it is not supported and must be explicit. (string should be in those carats for my second approach, but the formatting is preventing me and IDK how to fix it).
  2. How can I declare the Task object so it appears in both conditional scopes? I tried declaring it with a constructor in the outer scope and there is no constructor for the object. I am able to declare the variable in the outer scope without a constructor, but then I get an error in the second scope that the variable is unassigned after assignment in the first scope. My intuition tells me to declare it in the outer scope as none, assign in the first conditional block, and then check for a value other than none in the second conditional block.
  3. Is my use of async/await appropriate/correct, or is there a bette way? Since I am calling a process, how would this affect the use of async?

Thanks in advance for the advice/assistance!

CodePudding user response:

Apart from the issue around returning the result, you should not use blocking functions in async code. Instead use async all the way:

async private void proceedClicked()
{    
    if (checkBox.Checked)
    {
        string cmd = ""; // some python command
        Task<string> pythonTask = runProcAsync(cmd);
    }

    // do some other processing

    if (checkBox.Checked)
    {
        var pythonResult = await pythonTask;
        // print result to a message box
    }
}

private async Task<string> runProcAsync(string cmd)
{
    ProcessStartInfo start = new ProcessStartInfo
    {
        FileName = "python.exe", // full path to python
        Arguments = cmd,
        UseShellExecute = false,
        RedirectStandardOutput = true,
    };
    using (Process process = Process.Start(start))
    {
        using (StreamReader reader = process.StandardOutput)
        {
            string result = await reader.ReadToEndAsync();
            return result;
        }
    }
}

CodePudding user response:

private Task runProcAsync(string cmd)
{
    return Task.Run(() =>
        {
            callPython(cmd);
        });
}

Should probably be:

private Task<string> runProcAsync(string cmd)
{
    // you should be able to omit <string> here, as long as the return type is explicitly a string
    return Task.Run<string>(() =>
        {
            return callPython(cmd);
        });
}

Feel free to review some of Microsoft's documentation on Tasks here and here, and their guide to Task-based asynchronous programming.

I noticed you are also trying to use Task.Result as if it were a method, when in fact it is a property of the Task class.

  • Related