Это немного сложно.Вы на правильном пути, чтобы избежать прерывания, поскольку это может привести к прерыванию потока в середине важной операции (запись в файл и т. Д.).Установка свойства 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
и т. Д.).Таким образом, вы не сможете отменить длительные вычисления в процессе, как если бы вы использовали более ручной подход, как в этом ответе.Это, безусловно, отличный инструмент, чтобы держать его в заднем кармане и может быть просто полезен в вашем конкретном сценарии.