2010-02-22 2 views
8

Я пытаюсь написать ThreadManager для моего приложения C#. Я создаю несколько потоков:
Один поток для моего автора текста.
Один поток, который отслеживает статистику.
Несколько потоков для выполнения большой последовательности вычислений (до 4 потоков на ядро, и я запускаю свое приложение на 2x четырехъядерном сервере).Завершение многопоточного приложения

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

Я хочу иметь одно место, где я «регистрирую» все мои протекторы, и когда приложение закрывается, я просто вызываю метод, и он проходит через все зарегистрированные потоки и отключает их.

Для этой цели я разработал следующий класс:

public class ThreadManager 
{ 
    private static Object _sync = new Object(); 
    private static ThreadManager _instance = null; 
    private static List<Thread> _threads; 
    private ThreadManager() 
    { 
     _threads = new List<Thread>(); 
    } 

    public static ThreadManager Instance 
    { 
     get 
     { 
      lock (_sync) 
      { 
       if (_instance == null) 
       { 
        _instance = new ThreadManager(); 
       } 
      } 
      return _instance; 
     } 
    } 

    public void AddThread(Thread t) 
    { 
     lock (_sync) 
     { 
      _threads.Add(t); 
     } 
    } 

    public void Shutdown() 
    { 
     lock (_sync) 
     { 
      foreach (Thread t in _threads) 
      { 
       t.Abort(); // does this also abort threads that are currently blocking? 
      } 
     } 
    } 
} 

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

+0

WOW! У Лота хорошие ответы ... сейчас трудно выбрать. Я дал галочку Нейту, потому что он был первым, и было бы несправедливо отнять его у него. Посмотрим, что говорят голоса. Я получаю что-то полезное от всех ваших ответов, поэтому, пожалуйста, проголосуйте за свои сердца. – Kiril

+0

Поведение потоков потоков по умолчанию такое же, как я предложил. Если вам все равно, что ваши потоки не закончатся, мое - самое простое решение. Очевидно, что другие ответы дают хорошие советы об управлении потоками, но вы конкретно заявляете, что убивающие потоки в середине вычислений в порядке. –

+0

Я согласен с тобой Нейтом;), я не собираюсь отобрать галочку, потому что это было бы несправедливо, и ты дал мне самый точный, быстрый и простой ответ ... Я хочу, чтобы другие люди получили свою справедливую кредит, поэтому я хочу, чтобы все голосовали за все хорошие ответы. – Kiril

ответ

16

Если вы установите потоки в фоновый поток, они будут убиты, когда приложение будет закрыто.

myThread.IsBackground = true; 

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

+0

Спасибо Nate, я все мои потоки устанавливаю в фоновый поток, когда я их создаю ... значит ли это, что мне вообще не нужно их прерывать? – Kiril

+0

Да, если они настроены на фоновые потоки, закрытие приложения заставит потоки выйти. http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground.aspx –

2

Единственный конкретный вопрос, я знаю об это одна: http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx

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

14

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

Кстати, это предмет моего блога на этой неделе.

http://blogs.msdn.com/ericlippert/archive/2010/02/22/should-i-specify-a-timeout.aspx

+1

Этот поток прерываний исключений блокируется во время выполнения блоков finally для меня. Спасибо, что добавил. –

+0

@ fatcat1111: В самом деле, ничто не гарантирует, что прерванная нить будет * когда-либо остановлена ​​в результате прерывания. Еще одна причина избежать Thread.Abort; это опасно * и * ненадежно. –

+0

Согласованный, безусловно, последний вариант курорта. –

3

Что делать, если AddThread вызывается, когда ваш Shutdown работает?

Когда завершение работы заканчивается, поток, ожидающий в AddThread, добавит новый поток в коллекцию. Это может привести к зависанию в вашем приложении.

Добавьте флаг bool, который вы когда-либо устанавливали в выключенном состоянии, чтобы защитить его.

bool shouldGoAway = false; 
public void AddThread(Thread t) 
{ 
    lock (_sync) 
    { 
     if(! shouldGoAway) 
      _threads.Add(t); 
    } 
} 

public void Shutdown() 
{ 
    lock (_sync) 
    { 
     shouldGoAway = true; 
     foreach (Thread t in _threads) 
     { 
      t.Abort(); // does this also abort threads that are currently blocking? 
     } 
    } 

Также не следует использовать статические элементы - для этого нет причин, так как у вас есть экземпляр Singleton.

.Abort() не прерывает потоки, которые блокируются в неуправляемом пространстве.Поэтому, если вы это сделаете, вам нужно использовать какой-то другой механизм.

1

Если вы не заботитесь о состоянии рабочего потока, то вы можете перебрать _thread и прервете:

void DieDieDie() 
{ 
    foreach (Thread thread in _thread) 
    { 
     thread.Abort(); 
     thread.Join(); // if you need to wait for the thread to die 
    } 
} 

В вашем случае вы, вероятно, можете просто прервать их все и завершение работы, поскольку они просто делают расчеты , Но если вам нужно дождаться операции записи базы данных или вам нужно закрыть неуправляемый ресурс, вам нужно либо поймать ThreadAbortException, либо передать потоки, чтобы убить себя изящно.

0

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

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