блокировка производительности в 2 разных сценариях - PullRequest
4 голосов
/ 23 декабря 2010

У меня есть класс с 2 встроенными списками, содержащими более 1000 элементов, подобных этому,

public class A
{
    private List<long> aList = new List<long>();
    private List<long> bList = new List<long>();
    void MethodThatUsesAList()
    {
      //Works with aList
    }
    void MethodThatUsesBList()
    {
      //Works with bList
    }
    void MethodThatUsesBoth()
    {
      //Works with both lists
    }
}

Теперь я хочу сделать этот класс безопасным для потока, и я вижу 2 различных сценария, использующих блокировки, сначала для создания

object _synchObject = new object()

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

    void MethodThatUsesBoth()
    {
       lock(aList){
           lock(bList){
                 //Works with both lists
           }
       }
    }

Ответы [ 3 ]

3 голосов
/ 23 декабря 2010

Здесь есть два отдельных вопроса:

  • Использовать ли один комбинированный замок или два отдельных замка
  • Использовать ли для блокировки отдельные объекты или сами списки (или их SyncRoot s)

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

Если вы хотите, чтобы MethodThatUsesAList и MethodThatUsesBList могли работать одновременно, вам понадобятся две отдельные блокировки. Затем вам нужно будет убедиться, что каждый раз, когда вы можете приобрести оба замка, вы приобретаете их в том же порядке. Это означает рассуждение обо всем коде внутри любого метода, который получает блокировку: вам нужно убедиться, что он не вызывает другой метод , который получает другую блокировку, например.

Если ваш конкретный сценарий вряд ли сильно пострадает от всех методов, эффективно блокируемых для других потоков каким-либо одним потоком, выполняющим любой из них, тогда я бы использовал один замок просто для простоты.

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

1 голос
/ 23 декабря 2010
void MethodThatUsesBoth()
{
   lock(((ICollection)aList).SyncRoot){
       lock(((ICollection)bList).SyncRoot){
             //Works with both lists
       }
   }
}
void MethodThatUsesAList()
{
   lock(((ICollection)aList).SyncRoot){

   }
}
void MethodThatUsesBList()
{
   lock(((ICollection)bList).SyncRoot){

   }
}

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

0 голосов
/ 23 декабря 2010

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

...