Утечка в памяти приложения .NET C#

столкнулся с утечкой памяти и не совсем понимаю где именно она происходит. Код в бесконечном цикле считывает входящий поток из другого приложения, которое запущено при помощи Process.

 class Program
{
    static Process app1 = null;
    static Process app2 = null;
    static void Main(string[] args)
    {

        Config.init();

        try
        {
            app2 = new Process();
            app2.StartInfo.FileName = Config.appfolder + "/someapp1.exe";
            app2.StartInfo.WorkingDirectory = Config.appfolder + "/";
            app2.StartInfo.UseShellExecute = false;
            app2.Start();


            app1 = new Process();
            app1.StartInfo.FileName = Config.appfolder + "/someapp2.exe";
            app2.StartInfo.WorkingDirectory = Config.appfolder;
            app1.StartInfo.UseShellExecute = false;
            app1.StartInfo.RedirectStandardOutput = true;
            app1.StartInfo.Arguments = "bin/main";
            app1.Start();

        }catch(Exception e)
        {
                Console.WriteLine("Ошибка");
        }

        while (true)
        {
            if(app1.HasExited | app1.Id == 0)
            {
                app1.Start();
            }

            if (app2.HasExited | app2.Id == 0)
            {
               Console.WriteLine("Упал FTP сервер");
              
            }
            String app1log = ReadStringFromConsole(app1.StandardOutput);
            if(app1log != "END")Console.WriteLine(app1log);
            app1log = null; 
        }

    }

Так как ReadLine() ждёт пока не появится строка, приложение зависало и пришлось извратиться так, по другому не совсем понятно как сделать

   static public string ReadStringFromConsole(StreamReader read)
    {
        Thread.Sleep(2000);
        string output = "END";
        Thread t = new Thread(() => output = read.ReadLine());
        t.Start();
        if (output != "END") return output;
        //t.Abort();
        return output;
    }

В отладчике мониторинг ресурса показывается постоянную прогрессию. За сутки съело 32гб

https://i.stack.imgur.com/oVknY.png


Ответы (1 шт):

Автор решения: aepot

Попробуйте реализацию с обработчиком события при получении данных. Не надо крутить в цикле 2 секунды, обработчик будет вызываться только когда получены данные.

Так же я добавил еще несколько минорных улучшений в код.

public class Program
{
    static void Main(string[] args)
    {
        Config.init();
        Process app1 = new Process();
        Process app2 = new Process();
        try
        {
            app2.StartInfo.FileName = Path.Combine(Config.appfolder, "someapp1.exe");
            app2.StartInfo.WorkingDirectory = Config.appfolder + "/";
            app2.StartInfo.UseShellExecute = false;
            app2.Start();

            app1.StartInfo.FileName = Path.Combine(Config.appfolder, "someapp2.exe");
            app1.StartInfo.WorkingDirectory = Config.appfolder + "/";
            app1.StartInfo.UseShellExecute = false;
            app1.StartInfo.RedirectStandardOutput = true;
            app1.OutputDataReceived += App1_DataReceived;
            app1.ErrorDataReceived += App1_DataReceived;
            app1.StartInfo.Arguments = "bin/main";
            app1.Start();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Ошибка при запуске процессов: " + ex.Message);
            return;
        }

        using (CancellationTokenSource cts = new CancellationTokenSource())
        {
            _ = ProcessWatchDog(app1, () => { app1.Start(); }, cts.Token);
            _ = ProcessWatchDog(app2, () => { Console.WriteLine("Упал FTP сервер"); }, cts.Token);
            while (true)
            {
                string command = Console.ReadLine();
                if (command == "exit")
                {
                    break;
                }
            }
            cts.Cancel();
            if (!app1.HasExited) app1.Kill();
            if (!app2.HasExited) app2.Kill();
            app1.WaitForExit();
            app2.WaitForExit();
            Console.WriteLine("Все процессы остановлены. Нажмите любую клавишу...");
            Console.ReadKey();
        }
    }

    private static async Task ProcessWatchDog(Process process, Action action, CancellationToken token)
    {
        try
        {
            await Task.Run(() =>
            {
                while (!token.IsCancellationRequested)
                {
                    if (!process.HasExited)
                        process.WaitForExit();
                    else
                        break; // если процесс не запущен, то и следить не за чем, выходим
                    token.ThrowIfCancellationRequested();
                    action?.Invoke();
                }
            });
        }
        catch (OperationCanceledException ex)
        {
            Console.WriteLine("WatchDog для " + Path.GetFileName(process.StartInfo.FileName) + " остановлен: " + ex.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Ошибка в WatchDog " + Path.GetFileName(process.StartInfo.FileName) + ": " + ex.Message);
        }
        Console.WriteLine("WatchDog для " + Path.GetFileName(process.StartInfo.FileName) + " завершен.");
    }

    private static void App1_DataReceived(object sender, DataReceivedEventArgs e)
    {
        string app1log = e.Data;
        if (app1log != "END") Console.WriteLine(app1log);
    }
}

Watch dog в переводе с английского - сторожевая собака. Метод в бесконечном цикле ждет когда процесс завершится, и предпринимает действие после завершения процесса, которое вы ему передали.

Я добавил возможность выхода из программы, в консоли просто написать "exit" и нажать Enter.

Вместо Kill() можете сделать RedirectStandardInput и отправить команду дочернему процессу на нормальный выход.

→ Ссылка