c # foreach с заблокированным свойством - PullRequest
1 голос
/ 08 января 2012

Хорошо, вот собственность, которую я имею

public List<String> names{
    get{
        lock(_names)
            return _names;
       }
   set{
       lock(_names)
           _names = value
      }
}
private List<String> _names;

и теперь скажите, что я делаю foreach для имен, подобных этому

foreach(String s in names)
{
    Console.WriteLine(s);
}

У меня вопрос: names заблокировано через все foreach, или оно только lock каждый раз, когда устанавливается s, затем разблокируется внутри foreach.

Если это сбивает с толку, скажите, что я пытаюсь сделать это

foreach(String s in names)
{
    lock(names)
        Console.WriteLine(s);
}

Я попаду в тупик?

Ответы [ 6 ]

4 голосов
/ 08 января 2012
lock(_names) return _names;

это сокращение от:

try
{
  Monitor.Enter(_names);
  return _names;
}
finally
{
  Monitor.Exit(_names);
}

что эквивалентно:

Monitor.Enter(_names);
Monitor.Exit(_names);
return _names;

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

3 голосов
/ 08 января 2012

Внутри _get заблокирован только один метод свойства:

    void Main()
    {
        Test test = new Test();
        test.CallMe();
    }


    public class Test {

        List<string> _names= new List<string>(); 

        public List<string> Names {
            get {
            Console.WriteLine("Lock");
            lock(_names) {
                Console.WriteLine("Exit");
                return _names;
            }

            }       

        }

        public void CallMe()
        {
            foreach(String s in Names)
            {
                Console.WriteLine(s);
            }
        }
 }

Вывод, если это

Lock
Exit

IL-код ясно показывает, что блокировка происходит внутри _getМетод собственности:

IL_0000:  newobj      UserQuery+Test..ctor
IL_0005:  stloc.0     
IL_0006:  ldloc.0     
IL_0007:  callvirt    UserQuery+Test.CallMe

Test.get_Names:        //GET METHOD OF THE PROPERTY
IL_0000:  ldstr       "Lock"
IL_0005:  call        System.Console.WriteLine
IL_000A:  ldc.i4.0    
IL_000B:  stloc.0     
IL_000C:  ldarg.0     
IL_000D:  ldfld       UserQuery+Test._names
IL_0012:  dup         
IL_0013:  stloc.2     
IL_0014:  ldloca.s    00 
IL_0016:  call        System.Threading.Monitor.Enter  //LOCK
IL_001B:  ldstr       "Exit"
IL_0020:  call        System.Console.WriteLine
IL_0025:  ldarg.0     
IL_0026:  ldfld       UserQuery+Test._names
IL_002B:  stloc.1     
IL_002C:  leave.s     IL_0038
IL_002E:  ldloc.0     
IL_002F:  brfalse.s   IL_0037
IL_0031:  ldloc.2     
IL_0032:  call        System.Threading.Monitor.Exit //UNLOCK
IL_0037:  endfinally  
IL_0038:  ldloc.1     
IL_0039:  ret         

Test.CallMe:     // CALLME METHOD CODE
IL_0000:  ldarg.0     
IL_0001:  call        UserQuery+Test.get_Names //ONCE !!
IL_0006:  callvirt    System.Collections.Generic.List<System.String>.GetEnumerator
IL_000B:  stloc.1     
IL_000C:  br.s        IL_001C
IL_000E:  ldloca.s    01 
IL_0010:  call        System.Collections.Generic.List<System.String>.get_Current
IL_0015:  stloc.0     
IL_0016:  ldloc.0     
IL_0017:  call        System.Console.WriteLine
IL_001C:  ldloca.s    01 
IL_001E:  call        System.Collections.Generic.List<System.String>.MoveNext
IL_0023:  brtrue.s    IL_000E
IL_0025:  leave.s     IL_0035
IL_0027:  ldloca.s    01 
IL_0029:  constrained. System.Collections.Generic.List<>.Enumerator
IL_002F:  callvirt    System.IDisposable.Dispose
IL_0034:  endfinally  
IL_0035:  ret         

Test..ctor:
IL_0000:  ldarg.0     
IL_0001:  newobj      System.Collections.Generic.List<System.String>..ctor
IL_0006:  stfld       UserQuery+Test._names
IL_000B:  ldarg.0     
IL_000C:  call        System.Object..ctor
IL_0011:  ret         
3 голосов
/ 08 января 2012

В этом сценарии ваша блокировка фактически ничего не делает. Геттер примерно расширяется до следующего

public List<string> names {
  get {
    Monitor.Enter(_names); 
    try {
      return _names;
    } finally {
      Monitor.Exit(_names);
    }
  }
}

Это означает, что доступ к ресурсу синхронизируется только на время, необходимое для чтения ссылки. Это ничего не сделает для защиты целостности List<string>.

Ссылка names читается только один раз в foreach, поэтому она не будет заблокирована в теле. Это не приведет к тупикам, но и для защиты не очень много.

3 голосов
/ 08 января 2012

Он не заблокирован внутри foreach.Он блокируется один раз на get ссылку, затем повторяется.(Кроме того, ваши операторы lock внутри свойства в настоящее время не нужны; эти операции являются атомарными.)

Скорее всего, вы должны блокироваться вне тела цикла и там, где коллекция изменена.


В ответ на ваши изменения, нет.Это не параллель foreach, поэтому цикл не может застрять сам по себе, не говоря уже о тупике.Еще раз, это просто потраченная впустую вычислительная мощность.

2 голосов
/ 08 января 2012

Цикл foreach на короткое время блокирует _names, чтобы получить List <>, но разблокирует до того, как запустит реальный цикл.

Блокировка на _names может быть не совсем правильной формой, я бы представил другой объект для блокировкиэто не изменяется фактической операцией установки, которая предположительно заблокирована.

2 голосов
/ 08 января 2012

Метод get_Names будет вызван только один раз, и блокировка будет завершена (будет no lock) во время итерации элементов.Вероятно, это не то, что вы намереваетесь, и вам, вероятно, следует пойти на более детальную блокировку, такую ​​как:

lock (someLockObject)
{
    foreach(String s in names)
    {
        Console.WriteLine(s);
    }
}

Когда вы блокируете внутри цикла, вы будете брать много блокировок одну за другой, что почтиконечно, не то, что вы хотите, так как работа цикла foreach больше не будет атомарной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...