Ожидаемый ThreadAbort для фонового потока

У меня есть следующее.

public static Thread testThread = new Thread(ThreadStart) {Name = "TestThread", IsBackground = true};
private void Form_Load()
{
    testThread.Start()
}
private static void ThreadStart()
{
    int count = 0;
    try
    {
        while (true)
        {
            count++;
        }
    }
    catch (Exception ex)
    {

        StreamWriter stream = new StreamWriter(File.OpenWrite("Exception.txt"));
        stream.WriteLine(count + "\n" + ex);
        stream.Flush();
        stream.Close();
    }
}

Когда я вызываю Thread.Abort(), я ловлю исключение и записываю в файл. Однако, если я вместо этого закрываю приложение, ничего не пишется. у меня тоже есть

AppDomain.CurrentDomain.UnhandledException +=
   new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
Application.ThreadException +=
   new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);

Но похоже, что исключение никогда не выбрасывается.

Я полагаю, добавление вопроса разумно.

Что происходит с работающим фоновым потоком при выходе из родительского процесса? Насколько я понимаю, для выхода из потока выбрасывается исключение ThreadAbortException. Если это так, как можно перехватить ThreadAbortException, чтобы очистить ресурсы, которые могут присутствовать в потоке?


person galford13x    schedule 05.02.2012    source источник
comment
Как вы начинаете ветку? Почему вы ожидаете, что ThreadAbort будет выброшен? А в чем вопрос?   -  person Chris Shain    schedule 06.02.2012
comment
Из всего, что я читал, когда приложение завершает работу, все запущенные фоновые потоки прерываются с помощью исключения ThreadAbortException. Я запускаю поток, используя Thread.Start(ThreadStart)   -  person galford13x    schedule 06.02.2012
comment
@Chris: Здесь я получил некоторую информацию. stackoverflow.com/questions/1354196/   -  person galford13x    schedule 06.02.2012
comment
Почему вы используете Thread.Abort(). Убивать треды плохо, м'кей? Лучше убедить их совершить самоубийство, чтобы не попасть в тюрьму.   -  person CodesInChaos    schedule 06.02.2012
comment
@CodeInChaos: это образовательное упражнение. Но вопрос к вам: вы бы рассматривали возможность сделать поток фоновым, а затем выйти из приложения? Я видел, как это делается во многих примерах и библиотеках, и всегда задавался вопросом, почему они не используют разные шаблоны для более изящного завершения потока. Именно поэтому я задаю вопрос сейчас.   -  person galford13x    schedule 06.02.2012


Ответы (3)


Прежде всего, приложение, вероятно, не закрывается, потому что вы не превращаете их в фоновые потоки. Я предполагаю, что диспетчер задач покажет копии вашего EXE-файла, работающего неожиданно. Перед вызовом Start необходимо установить для Thread.IsBackground значение true для объекта потока.

Во-вторых, ожидаемое поведение явно опровергается документация:

Примечание

Когда среда CLR останавливает фоновые потоки после завершения всех приоритетных потоков в управляемом исполняемом файле, она не использует System.Threading.Thread.Abort. Таким образом, вы не можете использовать ThreadAbortException, чтобы определить, когда фоновые потоки завершаются средой CLR.

РЕДАКТИРОВАТЬ:

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

person Chris Shain    schedule 05.02.2012
comment
Почему это не фоновый поток. Я устанавливаю свойство IsBackground = true при инициализации потока. Сейчас я дважды проверю, что это правда. - person galford13x; 06.02.2012
comment
Я дважды проверил во время отладки, и IsBackground для testThread на самом деле верен. - person galford13x; 06.02.2012
comment
В этом случае см. документацию, о которой я упоминал выше. MSDN выигрывает мнение какого-то парня о переполнении стека 99999 раз из 100000. - person Chris Shain; 06.02.2012
comment
Моя цель - в образовательных целях для понимания разницы между фоновыми и передними потоками. В прошлом я использовал библиотеки, которые полагались на фоновые потоки для выхода через выход из приложения. Что касается высвобождения ресурсов при выходе из приложения, то это не совсем так. У меня были приложения, которые открывали файлы, но преждевременно завершали работу, удерживая блокировку файла, даже если процесс не указан в TaskManager. Единственным способом снять блокировку было использование специальных инструментов или перезагрузка системы. - person galford13x; 06.02.2012
comment
AFAIK, это невозможно в Windows. Может ошибаться. Конечно, это невозможно с использованием конструкций ввода-вывода .NET. Единственная особенность фоновых потоков в .NET заключается в том, что они не поддерживают процесс в рабочем состоянии. - person Chris Shain; 06.02.2012
comment
Хорошо, что я неправильно понял часть сообщения в предыдущем stackoeverflow, так как там также была цитата из MSDN. После повторного чтения я понял, что на самом деле не было создано исключение для остановки потока, а только то, что поток был остановлен. Не указано, как это делается. - person galford13x; 06.02.2012
comment
У меня была программа, блокирующая файл в Windows даже после его выхода. Хотя я могу не видеть процесс, указанный в диспетчере задач, это может быть какой-то мошеннический/дочерний процесс, который был запущен, и я просто не знаю, что искать. используя что-то вроде technet.microsoft.com/en-us/sysinternals/bb896653.aspx, может позволить найти виновника и освободить ресурс. - person galford13x; 06.02.2012
comment
Ваша ссылка на документацию MSDN ближе всего к тому, что я искал, однако я хотел бы знать, как CLR выполняет завершение работы. Однако знание того, что я не могу обнаружить отключение фона, весьма полезно. Спасибо за вашу помощь. - person galford13x; 06.02.2012
comment
Рекомендуем ознакомиться с этой статьей о размещении CLR: msdn.microsoft. com/en-us/magazine/cc163567.aspx#S3. Это не очень подробно, но должно указать вам правильное направление. - person Chris Shain; 06.02.2012
comment
Спасибо, Крис, я проверю это. - person galford13x; 06.02.2012

Когда CLR завершает процесс, он не вызывает Thread.Abort или что-то подобное. Ваши методы потока не завершатся, как ваш основной метод.

  1. Первое, что он делает, когда вы покидаете основной метод или вызываете Environment.Exit, — это завершает все объекты с тайм-аутом (в .NET 2.0 это было 2 секунды), после чего он продолжит завершать приложение независимо от текущих ожидающих финализаторов.
  2. Затем вызываются критические финализаторы.
  3. Затем все потоки приостанавливаются, чтобы они не причиняли вреда во время закрытия среды CLR.
  4. Ваше приложение было закрыто.
person Alois Kraus    schedule 05.02.2012

Если свойство IsBackground вашего потока равно false , тогда ваш поток останется живым, даже когда главное окно вашего приложения будет закрыто.

Лучший способ контролировать время жизни фоновых потоков – создать дозорные, обычно реализуемые в виде volatile bool полей, которые код в потоке проверяет через равные промежутки времени (например, на каждой итерации). Поток должен прекратить выполнение, когда часовой сигнал указывает, что приложение завершает работу.

В следующем коде показано использование часового для завершения потока через 200 миллисекунд:

public static Thread testThread = new Thread(ThreadStart) 
{
    Name = "TestThread", 
    IsBackground = false    // Allow thread to terminate naturally
};

private static volatile bool isTerminating = false;   // Sentinel

private void Form_Load()
{
    testThread.Start();
    Thread.Sleep(200);      // Sleep 200 milliseconds
    isTerminating = true;   // Set sentinel to terminate thread
}

private static void ThreadStart()
{
    int count = 0;

    while (!isTerminating)   // Keep looping until sentinel is set
        count++;

    using (StreamWriter stream = new StreamWriter(File.OpenWrite("Result.txt")))
    {
        stream.WriteLine(count);
        stream.Flush();
    }
}

Изменить. Чтобы ответить на ваш последний вопрос: «Как можно перехватить ThreadAbortException, чтобы очистить ресурсы, которые могут присутствовать в потоке?» Вы можете использовать обычный блок catch. ThreadAbortException может быть перехвачено, как и любое другое исключение, но оно будет автоматически вызвано снова в конце блока catch. Однако, как упомянул Крис, если процесс завершается, ThreadAbortException вообще не поднимается.

person Douglas    schedule 05.02.2012
comment
Как показано, установлено значение true. Обратите внимание, что new Thread(ThreadStart) {isBackground = true}; Однако я еще раз проверю, чтобы убедиться, что это правда, прежде чем начинать тему. - person galford13x; 06.02.2012
comment
Я понимаю общий шаблон выполнения потока. Но это для более образовательных целей, чтобы понять, что происходит, когда фоновый поток выходит. Я использовал библиотеки, которые вместо того, чтобы использовать хорошие шаблоны потоков, полагаются на поток IsBackground для выхода из потока. - person galford13x; 06.02.2012