Как я могу симулировать сценарий зависания приложения? - PullRequest
3 голосов
/ 26 марта 2010

У меня есть приложение Windows Forms, которое само запускает разные потоки для выполнения разных видов работы. Иногда ВСЕ потоки (включая поток пользовательского интерфейса) зависают, и мое приложение перестает отвечать на запросы. Я решил, что это может быть проблема, связанная с сборщиком мусора, поскольку сборщик мусора временно заморозит все управляемые потоки. Чтобы убедиться, что только что управляемые потоки заморожены, я запускаю неуправляемый поток, который каждую секунду пишет в файл «heartbeat» с меткой времени, и на него это не влияет (т.е. он все еще выполняется):

public delegate void ThreadProc();

[DllImport("UnmanagedTest.dll", EntryPoint = "MyUnmanagedFunction")]
public static extern void MyUnmanagedFunction();

[DllImport("kernel32")]
public static extern IntPtr CreateThread(
    IntPtr lpThreadAttributes,
    uint dwStackSize,
    IntPtr lpStartAddress,
    IntPtr lpParameter,
    uint dwCreationFlags,
    out uint dwThreadId);    

uint threadId;
ThreadProc proc = new ThreadProc(MyUnmanagedFunction);
IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(proc);
IntPtr threadHandle = CreateThread(IntPtr.Zero, 0, functionPointer, IntPtr.Zero, 0, out threadId);

Мой вопрос: как мне смоделировать эту ситуацию, когда все управляемые потоки приостановлены, но неуправляемые продолжают вращаться?

Мой первый удар :

private void button1_Click(object sender, EventArgs e) {
    Thread t = new Thread(new ThreadStart(delegate {
        new Hanger();
        GC.Collect(2, GCCollectionMode.Forced);
    }));
    t.Start();
}
class Hanger{
    private int[] m_Integers = new int[10000000];
    public Hanger() { }
    ~Hanger() { Console.WriteLine("About to hang...");

    //This doesn't reproduce the desired behavior
    //while (true) ;

    //Neither does this
    //Thread.Sleep(System.Threading.Timeout.Infinite); 
    }
}

Заранее спасибо !!

Ответы [ 4 ]

1 голос
/ 27 марта 2010

Финализаторы выполняются одновременно с "нормальным" исполнением потока. Обычно мы говорим, что GC запускает финализаторы, но было бы правильнее, если бы GC определял, какие экземпляры имеют финализаторы, которые должны быть запущены, и сохраняет их в выделенной очереди. (Скрытый) поток извлекает экземпляры из очереди и запускает финализаторы. Такой асинхронизм необходим, например, потому что финализаторы могут сами распределять память и потенциально запускать GC. Есть и другие веские причины , почему финализаторы обязательно асинхронны.

Суть в том, что вы не можете изменить, начиная с ~Hanger(), то, что виртуальная машина делает во время паузы GC, потому что поток, который фактически будет запускать ~Hanger(), также будет приостановлен в это время.

0 голосов
/ 17 апреля 2010

С учетом ответа Марека, это похоже на проблему проектирования с моделью параллелизма, которую вы используете. Это проблема проектирования, которую вы не можете эффективно решить путем тестирования.

Мой совет - внимательно рассмотреть модель параллелизма, которую вы используете, и соответствующим образом исправить дизайн. Начните с изучения необходимых условий для тупика, например ::1003*

  1. Какие у вас взаимные исключения?
  2. Какие дополнительные ресурсы требуются вашим процессам (которые уже используют некоторые ресурсы)?
  3. Какие ресурсы должны быть явно освобождены, если процесс использует их?

Принимая это во внимание, если у вас есть круговые структуры распределения ресурсов, вы попадаете в вероятную тупиковую ситуацию.

0 голосов
/ 17 апреля 2010

Проблема на самом деле связана с сборщиком мусора. После многих дней отладки и анализа дампов памяти с помощью WinDbg мы поняли, что возникла тупиковая ситуация, но она была вызвана одновременным сбором GC. Изменение GC для одновременного сбора решило нашу проблему.

0 голосов
/ 27 марта 2010

Я понимаю, что это не отвечает на ваш вопрос, но я подозреваю, что в вашем коде тупик, а не странная проблема GC.

Я бы посоветовал проверить ваш код на наличие взаимоблокировок, особенно косвенных случаев, таких как Control.Invoke вызовы при обновлении пользовательского интерфейса из фоновых потоков. Убедитесь, что вы не удерживаете блокировку при вызове Invoke - это может привести к неожиданным тупикам (как если бы ожидался какой-либо тупик:))

...