gpt4 book ai didi

c# - 异步捕获多个进程的输出

转载 作者:太空宇宙 更新时间:2023-11-03 10:34:41 26 4
gpt4 key购买 nike

我有一个 Windows 窗体应用程序可以同时启动最多 X 个控制台进程。我从每个对象中获取 JSON 输出(一个对象)并对其进行解析以获取统计信息。我正在跟踪我启动了多少个进程并在 OutputDataReceived() 中捕获它们的输出.我将输出附加到 ConcurrentBag<object> 中使用进程 ID 作为键。

我的 JSON 经常以两个对象的形式结束,这会引发解析错误。我不确定如何在同一对象中得到来自两个不同进程的数据。就好像 OutputDataReceived()事件正在从与其报告的 ID 不同的进程中获取数据。我尝试在没有任何运气的情况下实现一些锁定(这对我来说有点新,因为我来自经典 VB 背景)。

这里是一些相关的代码:

private object _lockObj = new object();
private ConcurrentBag<ProcData> _procDatas;

// This is called up to X times
private void LaunchProc(int itemId)
{
var proc = new Process();
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.ErrorDialog = false;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.EnableRaisingEvents = true;
proc.Exited += proc_Exited;
proc.OutputDataReceived += proc_OutputDataReceived;
proc.ErrorDataReceived += proc_ErrorDataReceived;
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.StartInfo.FileName = "someapp.exe";
proc.StartInfo.Arguments = "/id=" + itemId;
proc.Start();

proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
}

// I assume I'm screwing something up here since this is the only place where I set OutputData
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
var proc = sender as System.Diagnostics.Process;

if (proc == null) return;
if (e.Data == null) return;

lock (_lockObj)
{
var item = _procDatas.FirstOrDefault(pi => pi.Id == proc.Id);
if (item == null)
_procDatas.Add(new ProcData() {Id = proc.Id, OutputData = e.Data});
else
item.OutputData += e.Data;
}
}

void proc_Exited(object sender, EventArgs e)
{
var proc = sender as System.Diagnostics.Process;
ProcMessage procMsg = null;
lock (_lockObj)
{
var procInfo = _procDatas.FirstOrDefault(pi => pi.Id == proc.Id);
// JSON is parsed here and error is thrown because of multiple objects (eg {"ProcessId":1,"Msg":"Success"}{"ProcessId":2,"Msg":"Success"})
procMsg = new ProcMessage(procInfo.OutputData);
}
}

public class ProcData
{
public int Id { get; set; }
public string OutputData { get; set; }
public string ErrorData { get; set; }
}

在此先感谢您帮助解决此问题或提出不同(更好)的方法。

最佳答案

ID过程 can get reused‌​ .因此,我建议不要在进程退出后使用进程 ID 作为标识符。

相反,您可以使用 itemId 作为标识符,将流程输出与其相关联,并将流程和 itemId 封装在某个容器中,例如(经过轻微测试,似乎还可以):

public class ProcessExitedEventArgs<TKey> : EventArgs
{
public ProcessExitedEventArgs(TKey key, string[] output)
{
this.Key = key;
this.Output = output;
}

public TKey Key { get; private set; }
public string[] Output { get; private set; }
}

public delegate void ProcessExitedEventHandler<TKey>(object sender, ProcessExitedEventArgs<TKey> e);

public class ProcessLauncher<TKey>
{
public string FileName { get; private set; }
public string Arguments { get; private set; }
public TKey Key { get; private set; }

object locker = new object();
readonly List<string> output = new List<string>();
Process process = null;
bool launched = false;

public ProcessLauncher(string fileName, string arguments, TKey key)
{
this.FileName = fileName;
this.Arguments = arguments;
this.Key = key;
}

public event ProcessExitedEventHandler<TKey> Exited;

public bool Start()
{
lock (locker)
{
if (launched)
throw new InvalidOperationException();
launched = true;
process = new Process();
process.StartInfo.CreateNoWindow = true;
process.StartInfo.ErrorDialog = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(proc_Exited);
process.OutputDataReceived += proc_OutputDataReceived;
process.ErrorDataReceived += proc_ErrorDataReceived;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.FileName = FileName;
process.StartInfo.Arguments = Arguments;
try
{
var started = process.Start();

process.BeginErrorReadLine();
process.BeginOutputReadLine();
return started;
}
catch (Exception)
{
process.Dispose();
process = null;
throw;
}
}
}

void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
// Fill in as appropriate.
Debug.WriteLine(string.Format("Error data received: {0}", e.Data));
}
}

void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data == null)
return;
lock (locker)
{
output.Add(e.Data);
}
}

void proc_Exited(object sender, EventArgs e)
{
lock (locker)
{
var exited = Exited;
if (exited != null)
{
exited(this, new ProcessExitedEventArgs<TKey>(Key, output.ToArray()));
// Prevent memory leaks by removing references to listeners.
Exited -= exited;
}
}
var process = Interlocked.Exchange(ref this.process, null);
if (process != null)
{
process.OutputDataReceived -= proc_OutputDataReceived;
process.ErrorDataReceived -= proc_ErrorDataReceived;
process.Exited -= proc_Exited;
process.Dispose();
}
}
}

(此类还确保处理过程。)然后像这样使用它:

    public void LaunchProc(int itemId)
{
var launcher = new ProcessLauncher<int>("someapp.exe", "/id=" + itemId, itemId);
launcher.Exited += launcher_Exited;
launcher.Start();
}

void launcher_Exited(object sender, ProcessExitedEventArgs<int> e)
{
var itemId = e.Key;
var output = e.Output;

// Process output and associate it to itemId.
}

关于c# - 异步捕获多个进程的输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28260600/

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