После закрытия окна WPF с dotnetbrowser растёт потребление озу
не знаю как отловить утечку памяти на тестах с главного окна запускается около 10 потоков, в каждом создаётся окно wpf с элементом браузера dotnetbrowser и выполняются действия на определённых сайтах, после завершения действий вызывается dispose объекта браузера и закрывается окно из кода, ожидает около 2мин и запускается сново поток. Сразу же после закрытия окна wpf в течении 30 сек может вырасти потребление ОЗУ с 3ГБ до 15-20ГБ, потом снижается до уровня перед закрытием окна. Куда надо смотреть, чтобы найти исправление подобного?
код метода, который запускается в Thread:
public void Circle()
{
Instance instance = null;
string key = "keyxxxx";
string folder_program = AppDomain.CurrentDomain.BaseDirectory;
string end = new Random().Next(900000000).ToString();
string folderbin = "engineTest" + end;
string folder = "zapuskTest" + end;
string file_geo = "coordinates clear.txt";
string[] lines = File.ReadAllLines("keys.txt");
string WorkWord = lines[new Random().Next(lines.Length)];
lines = File.ReadAllLines("proxy.txt");
string Proxy = lines[new Random().Next(lines.Length)];
Proxy = lockp.TakeProxy();
int titanport = new Random().Next(11010, 12900);
instance = new Instance(this, WorkWord, key, false, Proxy, null, titanport, folder, folderbin, "", 768, 1366, file_geo);
//I = instance;
AllInstance.Add(instance);
string fullpathf = instance.FolderExist(folder);
string fullpathfb = instance.FolderBinary(folderbin);
try
{
instance.GoToMap();
}
catch
{
instance.TitanProxy.StopTitanProxy();
Console.WriteLine("Сбой ЗАДАЧИ------------------------------");
}
foreach (Tab t in instance.AllTabs)
{
try
{
if (!t.browser.IsDisposed)
{
t.browser.Dispose();
}
t.Clear();
}
catch
{
}
}
try
{
while (!instance.engine.IsDisposed)
{
instance.engine.Dispose();
Thread.Sleep(5000);
}
}
catch
{
}
instance.ActiveTab.IW.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
instance.IW.Close();
Thread.Sleep(10000);
}
catch
{
Console.WriteLine("НЕ СМОГЛИ ЗАКРЫТЬ ОКНО-------------------------------");
}
}));
try
{
instance.Clear();
Thread.Sleep(10000);
while (!instance.isDisposed)
{
instance.Clear();
Thread.Sleep(10000);
}
}
catch
{
}
AllInstance.RemoveAll(x => x.isDisposed || x == null);
Thread.Sleep(30000);
}
код запуска потоков:
await Task.Run(() =>
{
int start = 0;
int finish = 1000;
List<Thread> allThreads = new List<Thread>();
for (int f = 0; f < countTh; f++)
{
Thread th = new Thread(Circle);
th.Start();
allThreads.Add(th);
start++;
Thread.Sleep(15000);
}
while (start < finish)
{
for(int h=0; h<countTh;h++)
{
if(allThreads[h].IsAlive)
{
Thread.Sleep(7000);
}
else
{
allThreads.Remove(allThreads[h]);
//GC.Collect();
Thread th = new Thread(Circle);
th.Start();
allThreads.Add(th);
h--;
start++;
Thread.Sleep(60000);
}
}
}
Ответы (1 шт):
Использовать Sleep для синхронизации различных компонент приложения - плохо, потому что вы как минимум жертвуете поток на это ожидание. Как максимум вешаете все приложение, если этот Sleep находится в основном UI потоке.
Есть SemaphoreSlim, с помощью которого можно реализовать запуск любого числа задач с ограничением количества одновременно работающих.
То есть код запуска можно переписать вот так.
Добавить вот такой метод
public async Task StartCircleAsync(SemaphoreSlim semaphore)
{
try
{
await Task.Run(() => Circle());
}
finally
{
semaphore.Release();
}
}
Внешний Task.Run надо убрать, получится вот так.
int start = 0;
int finish = 1000;
List<Task> tasks = new List<Task>();
using (SemaphoreSlim semaphore = new SemaphoreSlim(countTh))
{
for (int i = start; i < finish; i++)
{
await semaphore.WaitAsync();
Task task = StartCircleAsync(semaphore);
lock (tasks)
tasks.Add(task);
_ = task.ContinueWith(_ =>
{
lock (tasks)
tasks.Remove(task);
});
}
Task[] taskArray;
lock (tasks)
taskArray = tasks.ToArray();
await Task.WhenAll(taskArray);
}
Вот я уже избавился от пачки Sleep.
Далее уберите все Sleep из UI потока, например вот этот
instance.ActiveTab.IW.Dispatcher.BeginInvoke(() =>
{
try
{
instance.IW.Close();
//Thread.Sleep(10000);
}
catch (Exception ex)
{
Debug.WriteLine("НЕ СМОГЛИ ЗАКРЫТЬ ОКНО: " + ex.Message);
}
});
Он совершенно ничего не делает и никак вам не помогает, только вешает приложение. При этом пока выполняется Sleep окно остается открытым и закроется оно только после того как приложение отвиснет после сна.
Далее, файлы keys.txt и proxy.txt можно прочитать один раз при старте приложения в нужные структуры данных и потом просто использовать эти уже прочитанные данные.
new Random() создаете 4 раза, создавайте один раз.
Random rnd = new Random();
string end = rnd.Next(900000000).ToString();
string WorkWord = lines[rnd.Next(lines.Length)];
string Proxy = lines[rnd.Next(lines.Length)];
int titanport = rnd.Next(11010, 12900);
Далее, после вызова Dispose объект нельзя использовать, вообще никак и никогда
if (!t.browser.IsDisposed)
{
t.browser.Dispose();
}
//t.Clear();
Далее, вот этот страшный цикл
while (!instance.engine.IsDisposed)
{
instance.engine.Dispose();
Thread.Sleep(5000);
}
Содержит ту же самую проблему, переписать вот так
if (!instance.engine.IsDisposed)
{
instance.engine.Dispose();
}
Далее, вот этот фрагмент, обрабатывайте ошибки правильно, хотя-бы сообщайте себе, что за ошибка произошла.
try
{
instance.GoToMap();
}
catch (Exception ex)
{
instance.TitanProxy.StopTitanProxy();
Debug.WriteLine("Сбой ЗАДАЧИ: " + ex.Message);
}
Как вы уже поняли, класс Console не следует использовать в отладочных целях в графических приложениях, используйте для вывода в Output окно студии класс Debug.
Чтобы копать дальше, надо знать подробнее внутреннюю структуру вашего приложения и его назначение. В любом случае, после применения рекомендаций, озвученных выше, должно стать лучше.