gpt4 book ai didi

c# - 执行进程的 C# 类中的异步方法

转载 作者:行者123 更新时间:2023-11-30 20:36:02 24 4
gpt4 key购买 nike

我有一个关于 this post 的后续问题.在我的版本中,我有以下要异步的。这是我所拥有的:

    public virtual Task<bool> ExecuteAsync()
{
var tcs = new TaskCompletionSource<bool>();
string exe = Spec.GetExecutablePath();
string args = string.Format("--input1={0} --input2={1}", Input1, Input2);

try
{
var process = new Process
{
EnableRaisingEvents = true,
StartInfo =
{
UseShellExecute = false,
FileName = exe,
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDir = CaseDir
}
};
process.Exited += (sender, arguments) =>
{
if (process.ExitCode != 0)
{
string errorMessage = process.StandardError.ReadToEndAsync();
tcs.SetResult(false);
tcs.SetException(new InvalidOperationException("The process did not exit correctly. Error message: " + errorMessage));
}
else
{
File.WriteAllText(LogFile, process.StandardOutput.ReadToEnd());
tcs.SetResult(true);
}
process.Dispose();
};
process.Start();
}
catch (Exception e)
{
Logger.InfoOutputWindow(e.Message);
tcs.SetResult(false);
return tcs.Task;
}
return tcs.Task;
}
}

在这里Spec, Input1, Input2, CaseDir, LogFile是 ExecuteAsync 是其方法的类的所有成员。这样使用它们可以吗?我正在努力的部分是:

  1. 我似乎无法在方法定义 ( public virtual async Task<bool> ExecuteAsync() ) 中使用 async 关键字,而不出现我需要 await 的警告关键字,而我在过程的 lambda 表达式中确实有一个。我什至需要在方法定义中使用 async 关键字吗?我见过所谓的异步示例,但他们不使用它,例如this one .如果我把它拿出来编译,但我可以异步使用它吗?
  2. 是我在lambda表达式中使用async关键字和对应的await process.StandardError.ReadToEndAsync() OK 进程里面的 lambda 表达式?在 this example , 他们不使用 async await在相应的行,所以我想知道他们是如何逃脱的?不会遗漏它使其阻塞,因为我被告知方法 ReadToEnd阻塞?
  3. 我的电话是File.WriteAllText(LogFile, process.StandardOutput.ReadToEnd())要渲染整个方法阻塞?如果是这样,我怎样才能避免这种情况(如果可能的话)?
  4. 异常处理有意义吗?我应该知道应用程序记录器方法的任何细节吗 Logger.InfoOutputWindow我在 catch 中使用过阻止?
  5. 最后,为什么 process.Exited事件总是出现在 process.Start() 之前在我遇到的所有例子中?我可以把 process.Start()process.Exited 之前事件?

感谢任何想法,并提前感谢您的兴趣和关注。

编辑#1:

对于上面的#3,我有一个想法,部分基于下面@René Vogt 的评论,所以我进行了更改以移动 File.WriteAllText(...)。在else {}里面打电话process.Exited 的 block 事件。也许这解决了 #3。

编辑#2:

我做了最初的更改列表(现在更改了代码片段),基本上删除了 async函数定义中的关键字和 await process.Exited 中的关键字基于@René Vogt 的原始评论的事件处理程序。还没有尝试过他最近的变化。当我运行时,我得到一个异常:

A plugin has triggered error: System.InvalidOperationException; An attempt was made to transition a task to a final state when it had already completed.

应用日志调用栈如下:

UNHANDLED EXCEPTION:
Exception Type: CLR Exception (v4)
Exception Details: No message (.net exception object not captured)
Exception Handler: Unhandled exception filter
Exception Thread: Unnamed thread (id 29560)
Report Number: 0
Report ID: {d80f5824-ab11-4626-930a-7bb57ab22a87}
Native stack:
KERNELBASE.dll+0x1A06D RaiseException+0x3D
clr.dll+0x155294
clr.dll+0x15508E
<unknown/managed> (0x000007FE99B92E24)
<unknown/managed> (0x000000001AC86B00)
Managed stack:
at System.Threading.Tasks.TaskCompletionSource`1.SetException(Exception exception)
at <namespace>.<MyClass>.<>c__DisplayClass3.<ExecuteAsync>b__2(Object sender, EventArgs arguments)
at System.Diagnostics.Process.RaiseOnExited()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading._ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(Object state, Boolean timedOut)

最佳答案

  1. 您的方法签名中不需要 async,因为您没有使用 await。返回一个 Task 就足够了。调用者可能会await Task - 也可能不会,这与您的方法无关。

  2. 不要在该 lambda 上使用 async 关键字,也不要在该 lambda 内使用异步 ReadToEnd。如果您在事件处理程序真正完成之前从该事件处理程序返回,则很难预测会发生什么。不管怎样,你想完成那个方法。它在进程退出时调用,无需使此 async

  3. 这里和(2)一样。我认为可以在此事件处理程序中“同步”执行此操作。它只会阻止此处理程序,但处理程序会在进程退出后调用,所以我想这对你来说没问题。

  4. 您的异常处理看起来不错,但我会在 Exited 事件处理程序中添加另一个 try/catch block 。但这不是基于知识,而是基于任何地方都可能出错的经验:)


为了更好地获取标准和错误输出,我建议订阅 ErrorDataReceivedOutputDataReceived 事件并填充 StringBuilder与接收到的数据。

在您的方法中,声明两个 StringBuilder:

StringBuilder outputBuilder = new StringBuilder();
StringBuilder errorBuilder = new StringBuilder();

并在实例化 process 后立即订阅事件:

process.OutputDataReceived += (sender, e) => outputBuilder.AppendLine(e.Data);
process.ErrorDataReceived += (sender, e) => errorBuilder.AppendLine(e.Data);

然后你只需要在你调用process.Start()之后立即调用这两个方法(之前是不行的,因为stdout和stderr不是尚未打开):

process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();

在您的 Exited 事件处理程序中,您可以调用 outputBuilder.ToString()(或分别为 errorBuilder.ToString())而不是 ReadToEnd 一切正常。

不幸的是,也有一个缺点:如果过程非常非常快,理论上您的 Exited 处理程序可能会在那些 Begin*ReadLine 调用之前被调用。不知道如何处理,但这不太可能发生。

关于c# - 执行进程的 C# 类中的异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37349973/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com