2013-06-07 4 views
6

У меня есть несколько очередей, к которым обращаются несколько потоков. Для достижения потокобезопасности, я сделал следующее:Могу ли я использовать словарные элементы в качестве объектов блокировки?

private static Dictionary<string, Queue<string>> MyQueues = new Dictionary<string, Queue<string>>(); 

public static string GetNextQueueElementForKey(string key) 
{ 
    string res = string.Empty; 

    if (MyQueues.Keys.Contains(key)) 
    { 
     Queue<string> queue = MyQueues[key]; 
     lock (queue) 
     { 
      if (queue.Count() > 0) 
      { 
       res = queue.Dequeue(); 
      } 
     } 
    } 

    return res; 
} 

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

+0

Ваш вопрос больше похож на заявление. Каков ваш вопрос снова? –

+0

Вы не блокируете объекты, вы блокируете * ссылки на объекты *. То есть, другими словами, меняется ли значение значения объекта на блокировку. – Nolonar

+0

@Nolonar нет, вы блокируете сам объект. У вас может быть 17 ссылок на один и тот же объект - все они будут блокировать одно и то же. –

ответ

6

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

В данном случае это выглядит, как вы получили контроль над очередями, так что вы знаете, что они не будут использоваться другим кодом, но это возможно, что код внутри Queue<T> зафиксирует на this. Скорее всего, это не так, но о том, о чем я буду беспокоиться.

В принципе, я хочу, чтобы .NET не применял подход Java к «монитору для каждого объекта». Я желаю, чтобы Monitor был экземпляром класса.

(я предполагаю, что вы на самом деле только чтение из словаря из нескольких потоков? Это не безопасно использовать словари для многопоточного чтения/записи.)

+0

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

+1

Но использование простого 'System.Object' не работает для неизвестного числа объектов блокировки. – Ben

+0

@Ben: Вы можете использовать словарь выделенных объектов блокировки, по одному для каждого экземпляра 'Queue '. Я до сих пор не понимаю, почему вы используете 'Queue ' вместо ['ConcurrentQueue '] (http://stackoverflow.com/a/16984743/45914). – jason

2

Тот факт, что она является элементом в словарь в значительной степени неактуальен - до тех пор, пока он является ссылочным типом (который Queue<string>равен) - тогда каждая очередь, когда она извлекается из словаря, будет таким же экземпляром object каждый раз. Это означает, что он будет работать вполне разумно с блокировкой на уровне очереди.

Так что в принципе: да, это должно работать нормально - до тех пор, пока Enqueue выполняет ту же самую блокировку в очереди. Как отмечает Джон, вы должны сделать это еще один вопрос.

Лично я до сих пор придерживаюсь мнения, что Monitor должен был быть нестатическим, и что вы могли бы блокировать только Monitor экземпляров, а не любые object.

1

Итак, мой вопрос заключается в том, что если блокировка объекта, содержащегося в словаре, будет работать - при условии, что значение ключа (очереди) никогда не будет изменено.

Глядя на ваш код здесь:

lock (queue) { 
    if (queue.Count() > 0) { 
     res = queue.Dequeue(); 
    } 
} 

Вы можете, но я бы не сделать это. Вы должны never lock on the object itself, так как вы можете конкурировать с другими тегами кода, которые будут блокироваться на том же самом объекте, включая Queue<T> (кто мог бы заблокировать this).

Итак, как минимум, вы должны создать выделенный объект блокировки для каждой очереди.

Но есть ли причина, по которой вы не используете ConcurrentQueue<T>? Это было бы самым простым решением, и он переместил бы бремя получения права на Рамку.

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