Потоки увеличиваются ненормально в службе Linux

У меня есть служба, которая работает в Linux под SystemD, но компилируется и отлаживается в VS22 под Windows. Служба в основном является прокси-сервером для базы данных MariaDB10 в форме BackgroundWorker, обслуживающей клиентов через SignalR. Если я запускаю его в режиме восстановления в Windows, количество логических потоков остается в разумных пределах (приблизительно 20-25). См. рисунок ниже.

В Linux через несколько минут (к сожалению, я не могу дать вам больше информации... мне все еще нужно выяснить, что может измениться) количество потоков начинает постоянно увеличиваться каждую секунду.

см. рисунок здесь, прибывший уже к более чем 100 и все еще подсчитывающий:

Чтение увеличение количества текущих логических потоков / утечка стека потоков я получил подтверждение того, что CLR разрешает новые потоки, если другие не завершаются, но в настоящее время в коде нет никаких изменений при переходе с Windows на Linux.

Это HostBuilder с вызовом SystemD

 public static IHostBuilder CreateWebHostBuilder(string[] args)
        {
            string curDir = MondayConfiguration.DefineCurrentDir();
            IConfigurationRoot config = new ConfigurationBuilder()
 
                // .SetBasePath(Directory.GetCurrentDirectory())
                .SetBasePath(curDir)
                .AddJsonFile("servicelocationoptions.json", optional: false, reloadOnChange: true)
#if DEBUG
                   .AddJsonFile("appSettings.Debug.json")
 
#else
                   .AddJsonFile("appSettings.json")
#endif
                   .Build();
            return Host.CreateDefaultBuilder(args)
                .UseContentRoot(curDir)
                .ConfigureAppConfiguration((_, configuration) =>
                {
                    configuration
                    .AddIniFile("appSettings.ini", optional: true, reloadOnChange: true)
#if DEBUG
                   .AddJsonFile("appSettings.Debug.json")
 
#else
                   .AddJsonFile("appSettings.json")
#endif
                    .AddJsonFile("servicelocationoptions.json", optional: false, reloadOnChange: true);
                })
 
                .UseSerilog((_, services, configuration) => configuration
                    .ReadFrom.Configuration(config, sectionName: "AppLog")// (context.Configuration)
                    .ReadFrom.Services(services)
                    .Enrich.FromLogContext()
                    .WriteTo.Console())
 
                // .UseSerilog(MondayConfiguration.Logger)
                .ConfigureServices((hostContext, services) =>
                {
                    services
                    .Configure<ServiceLocationOptions>(hostContext.Configuration.GetSection(key: nameof(ServiceLocationOptions)))
                    .Configure<HostOptions>(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(30));
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    ServiceLocationOptions locationOptions = config.GetSection(nameof(ServiceLocationOptions)).Get<ServiceLocationOptions>();
                    string url = locationOptions.HttpBase + "*:" + locationOptions.Port;
                    webBuilder.UseUrls(url);
                })
                .UseSystemd();
        }

Тем временем я пытаюсь отследить все Monitor.Enter(), которые я использую для рендеринга последовательных конечных точек API, которые касаются состояния службы и внутренних структур, но в Windows, похоже, все в порядке.

Я начинаю задаваться вопросом, не связана ли проблема с вызовом SystemD. Я хотел бы знать, что на самом деле связано с вызовом UseSystemD(), но документации не так много. Я только что нашел [https://devblogs.microsoft.com/dotnet/net-core-and-systemd/] (https://devblogs.microsoft.com/dotnet/net-core-and-systemd/ ) Гленна Кондрона и несколько кратких заметок о MSDN.

РЕДАКТИРОВАТЬ 1: Для дальнейшей отладки я создал класс для сканирования пула потоков с помощью ClrMd. У моей основной службы есть сердцебиение (странно, оно называется Ping) следующим образом (а не добавление в processTracker.Scan()):

private async Task Ping()
    {
        await _containerServer.SyslogQueue.Writer.WriteAsync((
            LogLevel.Information,
            $"Monday Service active at: {DateTime.UtcNow.ToLocalTime()}"));
        string processMessage = ProcessTracker.Scan();
        await _containerServer.SyslogQueue.Writer.WriteAsync((LogLevel.Information, processMessage));
        _logger.DebugInfo()
            .Information("Monday Service active at: {Now}", DateTime.UtcNow.ToLocalTime());
    }

где идентификатор processTrackes построен следующим образом:

 public static class ProcessTracker
    {
        static ProcessTracker()
        {
        }

        public static string Scan()
        {
            // see https://stackoverflow.com/questions/31633541/clrmd-throws-exception-when-creating-runtime/31745689#31745689
            StringBuilder sb = new();
            string answer = $"Active Threads{Environment.NewLine}";
            // Create the data target. This tells us the versions of CLR loaded in the target process.
            int countThread = 0;

            var pid = Process.GetCurrentProcess().Id;
            using (var dataTarget = DataTarget.AttachToProcess(pid, 5000, AttachFlag.Passive))
            {
                // Note I just take the first version of CLR in the process. You can loop over
                // every loaded CLR to handle the SxS case where both desktop CLR and .Net Core
                // are loaded in the process.
                ClrInfo version = dataTarget.ClrVersions[0];
                var runtime = version.CreateRuntime();
                // Walk each thread in the process.
                foreach (ClrThread thread in runtime.Threads)
                {
                    try
                    {
                        sb = new();
                        // The ClrRuntime.Threads will also report threads which have recently
                        // died, but their underlying data structures have not yet been cleaned
                        // up. This can potentially be useful in debugging (!threads displays
                        // this information with XXX displayed for their OS thread id). You
                        // cannot walk the stack of these threads though, so we skip them here.
                        if (!thread.IsAlive)
                            continue;

                        sb.Append($"Thread {thread.OSThreadId:X}:");
                        countThread++;
                        // Each thread tracks a "last thrown exception". This is the exception
                        // object which !threads prints. If that exception object is present, we
                        // will display some basic exception data here. Note that you can get
                        // the stack trace of the exception with ClrHeapException.StackTrace (we
                        // don't do that here).
                        ClrException? currException = thread.CurrentException;
                        if (currException is ClrException ex)
                            sb.AppendLine($"Exception: {ex.Address:X} ({ex.Type.Name}), HRESULT = {ex.HResult:X}");

                        // Walk the stack of the thread and print output similar to !ClrStack.
                        sb.AppendLine(" ------>  Managed Call stack:");
                        var collection = thread.EnumerateStackTrace().ToList();
                        foreach (ClrStackFrame frame in collection)
                        {
                            // Note that CLRStackFrame currently only has three pieces of data:
                            // stack pointer, instruction pointer, and frame name (which comes
                            // from ToString). Future versions of this API will allow you to get
                            // the type/function/module of the method (instead of just the
                            // name). This is not yet implemented.
                            sb.AppendLine($"           {frame}");
                        }
                    }
                    catch
                    {
                        //skip to the next
                    }
                    finally
                    {
                        answer += sb.ToString();
                    }
                }
            }
            answer += $"{Environment.NewLine} Total thread listed: {countThread}";
            return answer;
        }
    }

Все в порядке, в Windows он печатает много приятной информации в виде какого-то древовидного текстового представления.

Дело в том, что где-то требует Kernel32.dll, а в линуксе этого нет. Кто-нибудь может подсказать по этому поводу? Сервис изначально публикуется без инфраструктуры .NET, в режиме релиза, в Arch Linux64, в одном файле.

большое спасибо Алекс

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я нашел способ пропустить всю регистрацию того, что мне нужно, из простого сеанса отладки. Я не знал, что могу удаленно подключиться к процессу Systemd. Просто следуйте https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-dotnet-core-linux-with-ssh?view=vs-2022 для быстрого пошагового руководства. Единственным предварительным условием является то, чтобы служба находилась в режиме отладки и на хосте была установлена ​​среда выполнения NET, но это действительно все. Извините, что не знал этого раньше.

Алекс

Другие вопросы по теме