异步捕获多个进程的输出

吉姆·比利格(Jim Billig)

我有一个Windows窗体应用程序,可同时启动多达X个控制台进程。我从每个输出中获取JSON输出(一个对象),并将其解析为统计数据。我一直在跟踪我已经启动了多少个进程并在中捕获它们的输出OutputDataReceived()ConcurrentBag<object>使用流程ID作为关键字将输出附加到

通常,我的JSON最终会变成两个对象,从而引发解析错误。我不确定如何最终获得来自同一对象中两个不同进程的数据。好像OutputDataReceived()事件正在从与其报告的ID不同的过程中获取数据。我尝试实现一些锁定而没有任何运气(由于我来自Classic 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一个进程可以得到重用因此,我建议在进程退出后,不要将进程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.
    }

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

异步捕获多个进程的输出

来自分类Dev

如何在Powershell中异步捕获进程输出?

来自分类Dev

如何异步捕获长时间运行的 Python 子进程的输出

来自分类Dev

捕获python子进程的输出

来自分类Dev

捕获python子进程的输出

来自分类Dev

捕获Java中进程输出的简便方法

来自分类Dev

如何捕获子进程的输入和输出?

来自分类Dev

在C中平均多个进程的输出

来自分类Dev

powershell输出从进程列表派生的多个变量

来自分类Dev

在bash中捕获多个并行的grep输出

来自分类Dev

异步读取输出时,外部进程会阻塞主线程

来自分类Dev

异步读取输出时,外部进程会阻塞主线程

来自分类Dev

运行一个进程,捕获其输出并退出代码

来自分类Dev

在python子进程中捕获logger.info输出?

来自分类Dev

运行一个进程,捕获其输出并退出代码

来自分类Dev

使用boost创建多个子进程以异步运行

来自分类Dev

从调用线程异步执行的方法中的多个进程

来自分类Dev

读取标准输出并检查多个进程的状态VB.NET

来自分类Dev

如何阻止xargs严重合并多个进程的输出?

来自分类Dev

在Python中调用多个Linux进程并收集输出

来自分类Dev

Python:从多个子流程异步打印标准输出

来自分类Dev

捕获多个命令的控制台输出并写入txt

来自分类Dev

在进程运行时使用python异步读取控制台输出

来自分类Dev

使用其PID捕获具有Python的Linux进程的标准输出

来自分类Dev

在bash中调用python进程,然后将输出捕获到变量中

来自分类Dev

捕获python3中子进程的所有输出

来自分类Dev

如何使用 Haskell 海龟从进程中捕获 stdout 和 stderr 输出?

来自分类Dev

异步清理子进程

来自分类Dev

异步HttpWebRequest捕获WebException

Related 相关文章

热门标签

归档