После закрытия окна 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 шт):

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

Использовать 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.

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

→ Ссылка