C#进程执行的.py文件不等待完成

.py file executed by C# process not waiting to finish

我想从我的 C# 项目中 运行 .py 文件,并得到结果。 python 脚本发出 API 请求,returns 发出 auth_key 令牌,我想在我的 C# 代码中使用它。唯一的问题是,出于某种原因,C# 代码不会等待进程完成,因此并非每个帐户都有 auth_key。这是我的 C# 代码。

private static void GenerateTokens()
{
    var url = ConfigurationManager.AppSetting[GeSettingsNode() + ":ip"];

    for (int i = 0; i < accounts.Count; i++)
    {
         ProcessStartInfo start = new ProcessStartInfo();
         start.FileName = ConfigurationManager.AppSetting["PythonPath"];
         start.Arguments = string.Format($"python_operation_processor.py {accounts[i].client_key_id} {accounts[i].key_sercret_part} {url}");
         start.UseShellExecute = false;
         start.RedirectStandardOutput = true;
         Process process = Process.Start(start);
         using (StreamReader reader = process.StandardOutput)
         {
              accounts[i].auth_key = reader.ReadToEnd().Trim();
         }
     }
}

这是我的 Python 脚本 ( python_operation_processor.py ),它正在发出 API 请求。

if __name__ == '__main__':
    client_key_id = sys.argv[1]
    client_secret = sys.argv[2]
    API_URL = sys.argv[3]

    nonce = str(uuid.uuid4())

    d = datetime.datetime.now() - datetime.timedelta(hours=3)
    timestamp = d.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'

    signature = b64encode(hmac.new(b64decode(client_secret), msg=bytes(client_key_id + nonce + timestamp, 'utf-8'),
                                   digestmod=hashlib.sha256).digest()).decode('utf-8')

    r = requests.post(API_URL + '/v1/authenticate',
                      json={'client_key_id': client_key_id, 'timestamp': timestamp, 'nonce': nonce,
                            'signature': signature})
    if r.status_code != 200:
        raise Exception('Failed to authenticate: ' + r.text)

    auth_token = r.json()['token']
    print(auth_token)

你知道我如何等待每个进程的执行,并为每个帐户获取令牌吗?

我最近创建了类似的东西并以这个结束,因为虽然等待过程很容易,但要正确填充输出流却很棘手。

所提供的方法还允许您在应用程序中将输出显示到文本块或类似内容中。

如果你这样使用它,令牌将被写入 StringBuilder,并用作 return 值。

private async Task<string> RunCommand(string fileName, string args)
{
    var timeoutSignal = new CancellationTokenSource(TimeSpan.FromMinutes(3));
    ProcessStartInfo start = new ProcessStartInfo();
    start.FileName = fileName;
    start.Arguments = string.Format("{0}", args);
    start.RedirectStandardOutput = true;
    start.RedirectStandardError = true;
    start.UseShellExecute = false;
    start.CreateNoWindow = true;
    
    var sb = new StringBuilder();
    using (Process process = new Process())
    {
        process.StartInfo = start;
        process.OutputDataReceived += (sender, eventArgs) =>
        {
            sb.AppendLine(eventArgs.Data); //allow other stuff as well
        };
        process.ErrorDataReceived += (sender, eventArgs) => {};

        if (process.Start())
        {
            process.EnableRaisingEvents = true;
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();

            await process.WaitForExitAsync(timeoutSignal.Token);
            //allow std out to be flushed
            await Task.Delay(100);
        }
    }
    return sb.ToString();
}

要将其呈现为 UI 应用程序中的文本块,您需要:

  • 实施一个事件,表示已读取新行,这意味着转发 process.OutputDataReceived 事件。
  • 如果您考虑实时提要,请确保在 python 中刷新 stdio 缓冲区,将 flush 设置为 true:print(""hello world"", flush=True)

如果您使用的是较旧的 .net 版本;您可以按照此处所述实施 WaitForExitAsync: 作为扩展方法:

public static class ProcessHelpers
{
    public static Task<bool> WaitForExitAsync(this Process process, TimeSpan timeout)
    {
        ManualResetEvent processWaitObject = new ManualResetEvent(false);
        processWaitObject.SafeWaitHandle = new SafeWaitHandle(process.Handle, false);

        TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

        RegisteredWaitHandle registeredProcessWaitHandle = null;
        registeredProcessWaitHandle = ThreadPool.RegisterWaitForSingleObject(
            processWaitObject,
            delegate(object state, bool timedOut)
            {
                if (!timedOut)
                {
                    registeredProcessWaitHandle.Unregister(null);
                }

                processWaitObject.Dispose();
                tcs.SetResult(!timedOut);
            },
            null /* state */,
            timeout,
            true /* executeOnlyOnce */);

        return tcs.Task;
    }
}