Как лучше всего определить, что потоки остановлены?

У меня есть служба Windows с несколькими потоками, которые необходимо остановить до остановки службы. Я использую эту модель для остановки своих потоков и сигнализации о том, что они были остановлены:

ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
{
    Thread1Running = true;

    try
    {
        while (running)
        {
            AutoReset1.WaitOne(TimeSpan.FromSeconds(30.0));

            if (running)
            {
                // do stuff
            }
        }
    }
    finally
    {
        Thread1Running = false;
    }
}));

Когда приходит время остановить мою службу, я просто устанавливаю для ее запуска значение false и вызываю Set() для всех автосбросов. Это немедленно сигнализирует потокам о том, что они должны перестать спать или остановиться, как только они закончат обработку того, над чем они работают. Это работает очень хорошо. Но больше всего меня беспокоит то, что все остановилось. Вот что я делаю сейчас:

Int32 tries = 0;
while (tries < 5 && (Thread1Running || Thread2Running || Thread3Running))
{
    tries++;

    Thread.Sleep(TimeSpan.FromSeconds(5.0));
}

Основная причина, по которой мне это не нравится, заключается в том, что это основано на времени, и если один из моих потоков находится в середине длительной операции (что вполне вероятно), завершение работы может не завершиться за эти 25 секунд (и это очень важно, чтобы все эти потоки были закрыты к моменту завершения OnStop, когда запускается этот код). Я не могу удалить попытки, потому что если один из этих потоков зависнет (они не зависнут, но кто знает), то я действительно застрял, и служба никогда не остановится.

Есть ли лучший способ проверить, что все потоки остановлены, желательно тот, который не основан на времени? Или я обречен на какой-то тайм-аут, который, возможно, просто должен быть намного дольше (возможно, 10 попыток с 30-секундным сном)?


person Mike Pateras    schedule 29.09.2010    source источник
comment
Какую версию .NET вы используете?   -  person Conrad Frix    schedule 29.09.2010
comment
3.5, но можно и 4.0.   -  person Mike Pateras    schedule 30.09.2010


Ответы (1)


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

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

person Herms    schedule 29.09.2010
comment
Незначительное примечание. Перед вызовом Join вы должны добавить вызов RequestAdditionalTime, иначе SCM все равно отключит вас. - person Conrad Frix; 29.09.2010
comment
Из его примера кода видно, что его библиотека потоков — это пространство имен .net System.Threading. - person Reinderien; 29.09.2010
comment
Thread.Join — гораздо лучший подход. Функционально он делает то же самое, что и я, но намного чище. Он по-прежнему страдает от того, что он основан на времени, но я могу жить с этим. Любая идея, как я могу использовать его с ThreadPool? Или мне действительно нужно создавать темы? - person Mike Pateras; 29.09.2010
comment
На самом деле, я ожидаю, что у ThreadPool должен быть метод, который вы можете использовать для блокировки, пока его потоки не будут закрыты. По крайней мере, те, к которым я привык (java). - person Herms; 30.09.2010
comment
О, и Join на самом деле немного отличается, по крайней мере, насколько я помню. Это чище в том смысле, что позволяет выполнить дополнительную очистку. По крайней мере, из того, что я помню, когда имел дело с этим (может быть по-другому в .Net с GC и т. Д. Я вспоминаю свои дни C). - person Herms; 30.09.2010