Долгосрочный процесс приостановлен

У меня есть консольное приложение .NET 2.0, работающее на Windows Server GoDaddy VPS в среде Visual Studio 2010 IDE в режиме отладки (F5).

Приложение периодически зависает (как будто сборщик мусора временно приостановил выполнение), однако в редких случаях он никогда не возобновляет выполнение!

Я уже несколько месяцев дезориентирую это, и у меня заканчиваются идеи.

  • Приложение работает так быстро, как может (он использует 100% использование ЦП), но при нормальном приоритете. Он также многопоточен.
  • Когда приложение зависает, я могу разморозить его с помощью VS2010 IDE, приостановив/приостановив процесс (поскольку он работает в отладчике).
  • Местоположение последнего выполнения, когда я приостанавливаю замороженный процесс, кажется несущественным.
  • В то время как замораживание, использование ЦП по-прежнему на 100%.
  • После размораживания он работает отлично до следующего замораживания.
  • Сервер может запускать 70 дней между зависаниями, или это может сделать только 24 часа.
  • Использование памяти остается относительно постоянным; нет никаких признаков утечки памяти.

У кого-нибудь есть подсказки для диагностики того, что именно происходит?

11
nl ja de
хорошо, это было бы легко узнать, НЕ запуская его с отладчиком и посмотреть, не пойдет ли он.
добавлено автор Krumelur, источник
Никакой реальной помощи, но это меня озадачивает: почему вы используете приложение в отладчике из VS в течение 70 дней? Что не так, если вы запускаете его как сборку релизов, что даст вам больше производительности, меньше накладных расходов и может освободить вас от проблем, вызванных отладчиком, как вы их испытываете на данный момент?
добавлено автор Krumelur, источник
@hyde С тех пор я обновил до VS2012 Express, который содержит недостающие функции, кроме удаленной отладки (больше не работает в среде IDE). Мне еще предстоит заморозить, но может быть месяц или около того, пока это не повторится.
добавлено автор Mr. Smith, источник
@AmitBagga Я не вижу, что это очень возможно; я просто чувствую, что мне придется предоставить, прежде чем я смогу ожидать разумного анализа. Если у вас есть предположение, основанное на симптомах, которые я описал, не стесняйтесь кричать, это так же хорошо, как и у кого-то.
добавлено автор Mr. Smith, источник
@efkah lock и Thread.Start .
добавлено автор Mr. Smith, источник
@mjmarsh Это, к сожалению, версия VS2010 Express; в то время как в нем возможна многопоточная отладка, есть несколько недостающих (например, список Debug -> Windows -> Threads).
добавлено автор Mr. Smith, источник
@ Krumelur Ожидание потенциально 70 дней для следующего замораживания не звучит как простой способ узнать. В то же время нет отладчика, связанного с диагностикой любых других проблем, которые могут возникнуть.
добавлено автор Mr. Smith, источник
@Krumelur Я обычно совершаю патчи по коду на еженедельной основе, но был один момент времени, когда я не совершал никакого кода в течение пары месяцев. Я не хочу запускать приложение без приложения отладчика, если нет причин полагать, что отладчик может быть виновником.
добавлено автор Mr. Smith, источник
Похоже, пришло время получить полную версию VS2010, поэтому вы можете отлаживать это правильно ...
добавлено автор hyde, источник
+1 для @Krumelur - это немного похоже на проблему отладчика/среды. Запустите приложение без отладчика в отдельном ящике для тестирования.
добавлено автор Martin James, источник
+1 для @Krumelur помещает некоторую запись/трассировку и запускает ее за пределами VS. Захват вывода трассировки с помощью DbgView technet.microsoft.com/en-us/sysinternals/ bb896647.aspx
добавлено автор kenny, источник
Требуется немного больше информации, например, просмотрели ли вы список Debug -> Windows -> Threads и проверяете стек для всех потоков?
добавлено автор Mike Marshall, источник
Трудно сказать что-то с таким абстрактным описанием проблемы. Я бы предложил следующее: 1. Запустите программу без отладчика. Вы можете в любое время подключить отладчик для запуска программы для устранения неполадок. 2. Возьмите свалку своего процесса, когда он замерзает. Изучите дамп, используя визуальную студию или с расширением VSO WinDBG +. Или поставьте дамп где-нибудь и разместите ссылку на него здесь. Файлы PDB, созданные с помощью сборки, также понадобятся.
добавлено автор Sergey Zyuzin, источник
Использует ли ваш код одну и ту же стратегию блокировки ресурсов? Попробуйте записать в файл журнала (NLog хорошо работает), когда ваше приложение пытается приобрести, фактически приобретает и выпускает блокировки ресурсов, а затем, когда проблема обнаружена, проверьте, показывает ли журнал что-то вокруг того времени, в котором он произошел.
добавлено автор ajawad987, источник
Пожалуйста, вы можете поделиться кодом для общих ресурсов. Это может быть связано с тем, как были реализованы блокировки.
добавлено автор Amit Bagga, источник
Какое действие вы пытаетесь достичь в потоке. У вас есть петли в потоке, вы получаете доступ к базе данных в потоке
добавлено автор Amit Bagga, источник
им просто интересно, как вы реализовали многопоточность?
добавлено автор efkah, источник

3 ответы

Он также многопоточен

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

Его можно сузить немного дальше от информации, очевидно, что ваш процесс не полностью заморожен, так как он все еще потребляет 100% -ный процессор. У вас, вероятно, есть горячий цикл ожидания в вашем коде, цикл, который вращается по другому потоку, сигнализируя о событии. Это, вероятно, вызовет особенно неприятное множество тупиков, live-lock . Живые блокировки очень чувствительны к синхронизации, незначительные изменения в порядке выполнения кода могут привести к его блокировке. И снова возвращайся.

Live-locks чрезвычайно сложно отлаживать, поскольку попытка сделать это заставляет условие исчезнуть. Подобно прикреплению отладчика или разрыву кода, достаточно изменить время потока и вывести его из состояния. Или добавление операторов регистрации в ваш код, общую стратегию для отладки проблем с потоками. Что изменяет время из-за издержек записи, что, в свою очередь, может привести к тому, что live-lock полностью исчезнет.

Неприятный материал и невозможно получить помощь с такой проблемой с сайта, такого как SO, поскольку он чрезвычайно зависит от кода. Для выяснения причины часто требуется тщательный анализ кода. И нередко резкое переписывание. Удачи вам.

16
добавлено
Кроме того, объекты со слабым идентификатором (Thread, String, ParameterInfo и т. Д.).
добавлено автор Mr. Smith, источник
Это передовая практика до заблокировать частный экземпляр объекта , но это, конечно, не фундаментальный недостаток , чтобы не делать этого; если вы не следуете этой лучшей практике, тогда вам нужно очень знать об опасности блокировки (this) , lock (typeof (MyType)) и ( только тот, который когда-либо обманывал меня) lock (some_instance_of_a_string) .
добавлено автор Mr. Smith, источник
Тупик кажется скорее крайне , но единственным инструментом синхронизации, который я использую, являются операторы lock (и никогда не вложенные блокировки). Основной поток будет блокировать ресурс из рабочего потока до его доступа, а рабочий поток блокирует (и только блокирует) свой собственный ресурс перед его доступом. Нет никаких перекрестных рабочих нитей; основной поток - это единственный поток, который обращается к рабочему потоку, и он делает это только по одному за раз. Предполагая, что это так, как отладчик размораживает тупик между этими двумя потоками? Сроки не будут иметь значения, равно как и другие потоки.
добавлено автор Mr. Smith, источник
Хмя, это не помогает мне. Мне нужно увидеть код, и мне не на что смотреть. Но совершенно очевидно, что то, как вы говорите об этом, показывает фундаментальный недостаток вашего подхода. Вы никогда не блокируете ресурс. Ссылки, используемые в инструкции блокировки, всегда должны быть посвящены простым ссылкам объекта типа, единственным заданием которого является отслеживание состояния кода. Он никогда не должен быть «ресурсом», который вызывает тупик. Вы не можете блокировать данные, вы можете блокировать только код.
добавлено автор Hans Passant, источник
Это не стоит ничего, чтобы сделать это правильно. Но это не очень важно, думать об этой проблеме с точки зрения тупика не является продуктивным. Заблокированная программа не сжигает 100% ядро. Сильные знаки для живого замка, сосредоточиться на попытке найти объяснение, почему ваша программа идет полным ходом, но не добирается нигде, и у вас будет намного больше шансов диагностировать проблему.
добавлено автор Hans Passant, источник
@Мистер. Смит: как отладчик развяжет тупик между этими двумя потоками? -> Пример: Отладчик останавливается в одном процессе, давая другому процессу время. Как только вы войдете и нажмите F5, другой процесс будет закончен, Deadlock исчезнет, ​​все снова начнет плавное.
добавлено автор efkah, источник

Имеет ли приложение код «блокировки восстановления/предотвращения блокировки»? То есть, блокировка с тайм-аутом, а затем попытка снова, возможно, после сна?

Проверяет ли приложение коды ошибок (возвращаемые значения или исключения) и многократно повторяет попытку в случае ошибки?

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

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

Если ситуация будет происходить чаще, вы также можете использовать это, чтобы разделить ваш код и определить, какие циклы (потому что использование 100% CPU означает, что некоторые очень занятые циклы вращаются) несут ответственность. Но из-за редкости вопроса, я понимаю, вы будете счастливы, если проблема просто исчезнет на практике;)

2
добавлено
Нет кода восстановления/предотвращения взаимоблокировки. Есть несколько мест, где I catch {} , но я не повторяю; I catch {} , потому что эти места могут терпеть неудачу для обычных (и хорошо понятых) причин (например, сокет закрыт). В случаях, когда I catch {} , я не пытаюсь повторно отправить, я просто изящно терпит неудачу. Я был бы счастлив, если бы проблема исчезла, но я также согласился с ответом, в котором объяснялось, что это вызывает (даже если для этого не было никакого решения).
добавлено автор Mr. Smith, источник

Ну, три вещи здесь ...

Прежде всего, начните использовать GC сервера .NET: http://msdn. microsoft.com/en-us/library/ms229357.aspx . Это, вероятно, не сделает ваше приложение незаблокированным.

Во-вторых, если вы можете сделать это на своей виртуальной машине: проверьте наличие обновлений. Это всегда кажется очевидным, но я видел многочисленные случаи, когда простое обновление Windows фиксирует странные проблемы.

В-третьих, я хотел бы остановиться на жизни объекта, что может быть одной из проблем здесь. Это довольно длинная история, что происходит, так что несите меня.

Срок службы объекта в основном строится - сбор мусора - завершение. Все три процесса выполняются в отдельном потоке. GC передает данные в поток финализации, который имеет очередь, которая вызывает «деструкторы».

Итак, что, если у вас есть финализатор, который делает что-то странное, скажите что-то вроде:

public class FinalizerObject
{
    public FinalizerObject(int n)
    {
        Console.WriteLine("Constructed {0}", n);
        this.n = n;
    }

    private int n;

    ~FinalizerObject()
    {
        while (true) { Console.WriteLine("Finalizing {0}...", n); System.Threading.Thread.Sleep(1000); }
    }
}

Поскольку финализаторы работают в отдельном потоке, который обрабатывает очередь, наличие одного финализатора, делающего что-то глупое, является серьезной проблемой для вашего приложения. Вы можете увидеть это, используя вышеуказанный класс 2 раза:

    static void Main(string[] args)
    {
        SomeMethod();
        GC.Collect(GC.MaxGeneration);
        GC.WaitForFullGCComplete();
        Console.WriteLine("All done.");
        Console.ReadLine();
    }

    static void SomeMethod()
    {
        var obj2 = new FinalizerObject(1);
        var obj3 = new FinalizerObject(2);
    }

Обратите внимание на то, как вы закончите с небольшой утечкой памяти, и если вы удалите Thread.Sleep также с 100% -ным процессом процессора, даже если ваш основной поток все еще отвечает. Поскольку они разные потоки, отсюда очень легко заблокировать весь процесс - например, с помощью блокировки:

    static void Main(string[] args)
    {
        SomeMethod();
        GC.Collect(GC.MaxGeneration);
        GC.WaitForFullGCComplete();
        Thread.Sleep(1000);
        lock (lockObject)
        {
            Console.WriteLine("All done.");
        }
        Console.ReadLine();
    }

    static object lockObject = new Program();

    static void SomeMethod()
    {
        var obj2 = new FinalizerObject(1, lockObject);
        var obj3 = new FinalizerObject(2, lockObject);
    }

    [...]

    ~FinalizerObject()
    {
        lock (lockObject) { while (true) { Console.WriteLine("Finalizing {0}...", n); System.Threading.Thread.Sleep(1000); } }
    }

Поэтому я вижу, что вы думаете: «Вы серьезно?»; дело в том, что вы можете делать что-то подобное, даже не осознавая этого. Вот где «выход» входит в картину:

IEnumerable от «yield» на самом деле IDisposable и как таковой реализует шаблон IDisposable. Объедините реализацию «yield» с блокировкой, забудьте вызвать IDisposable, перечислив ее «MoveNext» и т. Д., И вы получите довольно неприятное поведение, которое отражает вышеизложенное. Тем более, что финализаторы вызывают из очереди финализации отдельным потоком (!). Объедините его с бесконечным циклом или потоком небезопасного кода, и вы получите довольно неприятное неожиданное поведение, которое будет срабатывать в исключительных случаях (когда память закончится или когда GC-вещи он что-то сделает).

Другими словами: я бы проверял ваши расходные материалы и финализаторы и очень критично относился к ним. Убедитесь, что «yield» имеет неявные финализаторы и убедитесь, что вы вызываете IDisposable из того же потока. Некоторые примеры того, о чем вы должны быть осторожны:

    try
    {
        for (int i = 0; i < 10; ++i)
        {
            yield return "foo";
        }
    }
    finally
    {
       //Called by IDisposable
    }

а также

    lock (myLock)//'lock' а также'using' also trigger IDisposable
    {
        yield return "foo";
    }
0
добавлено
@ Mr.Smith Хм, это усложняет ситуацию. Честно говоря, я был бы удивлен, если бы у вас не было каких-либо неявных финализаторов, они используются практически везде в .NET Framework ... что ваша реакция, безусловно, усложняет ситуацию. Я попытался бы использовать инструмент под названием «Управляемый стековый проводник» и удалять все следы стека всех потоков, когда он зависает, чтобы получить больше информации о том, что происходит точно. Также вы можете попробовать запустить его в Mono вместо MS .NET, чтобы убедиться, что это ошибка во время выполнения.
добавлено автор atlaste, источник
BTW: Я видел реальный случай жизни, когда «отладчик» размораживал вас, на самом деле выдавал (3) в сочетании с неуправляемым кодом (Socket IO в моем случае). Thread.Abort не работает в этих случаях, но ваш отладчик иногда делает некоторые странные вещи здесь. К сожалению, я не могу дать вам минимальный тестовый пример; его очень трудно воспроизвести.
добавлено автор atlaste, источник
Управляемый Stack Explorer выглядит так, как будто это было бы очень удобно; С тех пор я обновился до VS2012 Express (у которого есть правильный многопоточный отладчик), поэтому при следующем замораживании я должен иметь возможность просматривать состояние всех потоков. Что касается предложения об использовании Mono, для этого приложения требуется Microsoft SQL Server. Хотя код .NET Socket, который я написал, уже много лет работает в среде Mono (в других частях проекта) и стабилен.
добавлено автор Mr. Smith, источник
Я не знаю, какой GC моя среда не работает (но только потому, что мне все равно). Сказав это, MSDN рекомендует не использовать сервер GC на одноядерных машинах. Кроме того, он утверждает, что параллельный GC (который уменьшил бы необходимость блокировать все потоки) недоступен, если сервер GC используется с .NET 4.0 или ранее. Я имею тенденцию запускать Windows Update на компьютере каждую неделю, поэтому он не отстает от обновлений. Наконец, мой код не содержит финализаторов.
добавлено автор Mr. Smith, источник
Microsoft Stack Jobs
Microsoft Stack Jobs
1 788 участник(ов)

Work & freelance only Microsoft Stack. Feed https://t.me/Microsoftstackjobsfeed Чат про F#: @Fsharp_chat Чат про C#: @CSharpChat Чат про Xamarin: @xamarin_russia Чат общения:@dotnettalks

Про Windows
Про Windows
941 участник(ов)

Microsoft Windows и всё, что с этим связано. Список интересных групп и каналов: https://github.com/goq/telegram-list

Microsoft Developer Community Chat
Microsoft Developer Community Chat
584 участник(ов)

Чат для разработчиков и системных администраторов Microsoft Developer Community. __________ Новостной канал: @msdevru __________ Баним за: оскорбления, мат, рекламу, флуд, флейм, спам, NSFW контент, а также большое количество оффтоп тем. @banofbot