简体   繁体   English

如何在 C# 中使用 async 从称为进程的 python 脚本中获取我需要的结果?

[英]How can I use async in C# to get the result I need from a python script called as a process?

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.我已经使用 C# .NET 构建了一个 GUI,如果单击“PROCEED”按钮时在面板上选择了一个复选框,我需要合并一个 Python 脚本来调用。 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. python 脚本应该在后台运行,然后我想在 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.我了解如何从 C# 调用 Python,但对于如何使用异步函数收集和显示结果仍然有些模糊。 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.我根据 Microsoft 文档了解我需要使用 Task 对象,但我无法完全按照我的预期方式运行它。 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/以下是迄今为止我在代码中使用的文档以供参考: https ://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

This is my first time using async in C#.这是我第一次在 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?如何对齐 Task 和 Task 对象以使它们匹配? 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.我尝试使用 Task 然后 Task.Result() 来获取字符串,但是 Result() 不作为我声明的 Task var 的方法存在。 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.然后我尝试使用 Task<> 作为类型,但是 Task.Run() 抱怨隐式转换,因为它不受支持并且必须是显式的。 (string should be in those carats for my second approach, but the formatting is preventing me and IDK how to fix it). (对于我的第二种方法,字符串应该在那些克拉中,但是格式阻止了我和 IDK 如何修复它)。
  2. How can I declare the Task object so it appears in both conditional scopes?如何声明 Task 对象,使其出现在两个条件范围内? 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.我的直觉告诉我在外部范围内将其声明为 none,在第一个条件块中赋值,然后在第二个条件块中检查除 none 以外的值。
  3. Is my use of async/await appropriate/correct, or is there a bette way?我对 async/await 的使用是否合适/正确,还是有更好的方法? Since I am calling a process, how would this affect the use of async?由于我正在调用一个进程,这将如何影响异步的使用?

Thanks in advance for the advice/assistance!提前感谢您的建议/帮助!

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;
        }
    }
}
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 Task s here and here , and their guide to Task-based asynchronous programming .随意查看 Microsoft 的一些关于Task的文档, 这里这里,以及他们的基于任务的异步编程指南。

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.我注意到您还尝试将Task.Result用作方法,而实际上它是Task类的属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM