Утечка в памяти приложения .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 шт):
Попробуйте реализацию с обработчиком события при получении данных. Не надо крутить в цикле 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 и отправить команду дочернему процессу на нормальный выход.