Фон Потребительская резьба Пожизненное управление лучшими практиками

StackOverflow https://stackoverflow.com/questions/3558690

Вопрос

У меня есть библиотека классов C #, которая запускает фоновую потребительскую нить (Lazily), которая слушает задачи для завершения от производителя / потребительской очереди. Эта библиотека классов может использоваться из любого типа приложения .NET и в настоящее время используется под веб-сайтом ASP.NET MVC. Потребительская поток заблокирована большую часть времени, пока запрос не попадет в очередь. Должен быть только одна потребительская нить для любой данной домена приложения.

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

Как я могу решить эту проблему жизни потока с точки зрения библиотеки классов? Есть ли событие глобального отключения приложений, которое я могу завязать, чтобы изящно разорвать нить?

Прямо сейчас, поскольку он в домене ASP.NET MVC, это действительно не имеет значения, так как они никогда не оторваны изящными. Но теперь, когда я начинаю использовать его в задачах запланированных консольных приложений, предназначенных для прекращения, я хочу решить эту проблему раз и для всех. Приложение консоли не прекращается, поскольку потребительская нить все еще активна и заблокирована ожидание запросов. Я обнародовал публичное свойство статического потока в классе, чтобы выдать Abort() Звоните, когда приложение консоли выходит, но это, довольно откровенно, отвратительно.

Любые указатели будут оценены! Опять же, я не хочу писать Windows Forms или WPF или Console Application для решения проблемы. Хорошее универсальное решение, которое каждый потребитель библиотеки классов может быть использован, будет лучшим.

Это было полезно?

Решение

Установить нить IsBackground имущество true. Отказ Тогда он не помешает окончанию процесса.

http://msdn.microsoft.com/en-us/Library/system.threading.thread.isbackground.aspx.

Поочередно, вместо использования Abort, использовать Interrupt. Отказ Гораздо менее отвратительно.

Другие советы

Это немного сложно. Вы правы на треке, который вы хотите избежать прерваний, так как это может прекратить нить в середине важной операции (запись в файл и т. Д.). Установка IsBackground Собственность будет иметь тот же эффект.

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

public class CancellableBlockingCollection<T>
{
    private Queue<T> m_Queue = new Queue<T>();

    public T Take(WaitHandle cancel)
    {
        lock (m_Queue)
        {
            while (m_Queue.Count <= 0)
            {
                if (!Monitor.Wait(m_Queue, 1000))
                {
                    if (cancel.WaitOne(0))
                    {
                        throw new ThreadInterruptedException();
                    }
                }
            }
            return m_Queue.Dequeue();
        }
    }

    public void Add(T data)
    {
        lock (m_Queue)
        {
            m_Queue.Enqueue(data);
            Monitor.Pulse(m_Queue);
        }
    }
}

Обратите внимание, как Take Метод принимает а WaitHandle Это можно проверить для сигнала отмены. Это может быть одинаковую ручку ожидания, используемую в других частях вашего кода. Идея здесь состоит в том, что ручка ожидания получает опрос на определенный интервал внутри Take И если это сигнализируется, то весь метод бросает. Предположение в том, что бросается внутри Take Можно в порядке, так как он находится в безопасном месте, потому что потребитель не мог быть в середине важной операции. Вы можете просто позволить потоке распадаться на исключение в этот момент. Вот что может выглядеть ваша потребительская нить.

ManualResetEvent m_Cancel = new ManualResetEvent(false);
CancellableBlockingCollection<object> m_Queue = new CancellableBlockingCollection<object>();

private void ConsumerThread()
{
  while (true)
  {
    object item = m_Queue.Take(m_Cancel); // This will throw if the event is signalled.
  }
}

Есть различные другие способы отменить Take метод. Я решил использовать WaitHandle, но это легко можно сделать с помощью простого bool Флаг, например.

Обновлять:

Как Стивен указал в комментариях Thread.Interrupt По сути это уже. Это приведет к тому, что нить выбросит исключение на блокирующем вызове ... в значительной степени то, что я был после в этом примере, но с большим дополнительным кодом. Одна оговорка с Thread.Interrupt это то, что он работает только во время консервированных блокирующих звонков в .NET BCL (например WaitOne, так далее.). Таким образом, вы бы не сможете отменить долгое запуск вычисления, как вы, если бы вы использовали более ручный подход, как тот, который в этом ответе. Это определенно отличный инструмент, чтобы сохранить в вашем заднем кармане и может быть просто полезным в вашем конкретном сценарии.

Если вы используете .NET 4.0 A CancelationToken Может использоваться для сигнализации потока, настало время изящно закрыто. Это очень полезно, так как большинство команд щитов позволяют пропускать ее токен, и если нить находится в ожидании, когда поток отменяется, она автоматически выйдет из ожидания.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top