2010-10-20 2 views
4

У меня есть Список newJobs. Некоторые потоки добавляют элементы к этому списку, а другой поток удаляет из него элементы, если он не пуст. У меня ManualResetEvent newJobEvent, который установлен, когда элементы добавлены в список, и сбрасывается, когда элементы удаляются из него:Проблема многопоточности при проверке списка Count property

Добавление элементов в списке осуществляется следующим образом:

lock(syncLock){ 
    newJobs.Add(job); 
} 
newJobEvent.Set(); 

Работа удаление выполняется следующим образом:

if (newJobs.Count==0) 
    newJobEvent.WaitOne(); 
lock(syncLock){ 
    job = newJobs.First(); 
    newJobs.Remove(job); 
    /*do some processing*/ 
} 
newJobEvent.Reset(); 

Когда линия

job=newJobs.First() 
Выполняется

. Иногда я получаю исключение, что список пуст. Я думаю, что чек:

if (newJobs.Count==0) 
     newJobEvent.WaitOne(); 

также должны быть в заявлении блокировки, но я боюсь, тупиков на линии newJobEvent.WaitOne();

Как его решить?

Большое спасибо и извините за длинный пост!

ответ

2

Вы правы. Вызов WaitOne внутри замка может привести к тупиковой ситуации. И проверка, чтобы убедиться, что список пуст, должен быть сделан внутри блокировки, иначе может быть гонка с другим потоком, пытающаяся удалить элемент. Теперь ваш код выглядит подозрительно, как шаблон производителя-потребителя, который обычно реализуется с блокирующей очередью. Если вы используете .NET 4.0, вы можете воспользоваться классом BlockingCollection.

Однако позвольте мне пройти пару способов, которыми вы можете это сделать сами.Первый использует List и ManualResetEvent, чтобы продемонстрировать, как это можно сделать с использованием структур данных в вашем вопросе. Обратите внимание на использование петли while в методе Take.

public class BlockingJobsCollection 
{ 
    private List<Job> m_List = new List<Job>(); 
    private ManualResetEvent m_Signal = new ManualResetEvent(false); 

    public void Add(Job item) 
    { 
    lock (m_List) 
    { 
     m_List.Add(item); 
     m_Signal.Set(); 
    } 
    } 

    public Job Take() 
    { 
    while (true) 
    { 
     lock (m_List) 
     { 
     if (m_List.Count > 0) 
     { 
      Job item = m_List.First(); 
      m_List.Remove(item); 
      if (m_List.Count == 0) 
      { 
      m_Signal.Reset(); 
      } 
      return item; 
     } 
     } 
     m_Signal.WaitOne(); 
    } 
    } 
} 

Но это не так, как я бы это сделал. Я бы пошел с более простым решением ниже с использованием Monitor.Wait и Monitor.Pulse. Monitor.Wait полезен, потому что он может быть вызван внутри замка может. Фактически, предположим, что будет сделано таким образом.

public class BlockingJobsCollection 
{ 
    private Queue<Job> m_Queue = new Queue<Job>(); 

    public void Add(Job item) 
    { 
    lock (m_Queue) 
    { 
     m_Queue.Enqueue(item); 
     Monitor.Pulse(m_Queue); 
    } 
    } 

    public Job Take() 
    { 
    lock (m_Queue) 
    { 
     while (m_Queue.Count == 0) 
     { 
     Monitor.Wait(m_Queue); 
     } 
     return m_Queue.Dequeue(); 
    } 
    } 
} 
+0

Благодарим Брайана за подробный ответ. Это очистило мне всю эту проблему, которая для меня новая. Еще раз спасибо!!! – mayap

0

Не понимаю, почему удаление объекта в случае нулевых объектов должно дождаться добавления и затем удалить его. Это похоже на логику.

1

Не отвечаю на ваш вопрос, но если вы используете .NET framework 4, вы можете использовать новый ConcurrentQueue, который делает всю блокировку для вас.

Что касается Вашего вопроса:

один сценарий, который я могу думать причиной такой проблемы заключается в следующем:

  • Вставка резьбы входит в замок, вызывает newJob.Add, покидает замок.
  • Переключатель контекста к резьбе для удаления. Он проверяет пустоту, видит элемент, входит в заблокированную область, удаляет элемент, сбрасывает событие, которое еще не установлено.
  • Контекстный коммутатор обратно к вставке, событие установлено.
  • Контекстный переключатель обратно к резьбе для удаления. Он проверяет пустоту, не видит предметов, ждет событие - которое уже установлено, пытается получить первый элемент ... Bang!

Установите и перезапустите событие внутри замка, и все должно быть в порядке.

+0

большое спасибо !!!! – mayap

Смежные вопросы