Можно ли заблокировать () в закрытии?Как это выглядит в Lambdas и выводе кода? - PullRequest
3 голосов
/ 24 января 2011

Я читал этот вопрос и прочитал этот ответ

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

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

Нам нужно реализовать Lock () в этой ситуации?

Как бы это выглядело?

По словам Эрика Липперта, компилятор делает код похожим на this :

private class Locals
{
  public int count;
  public void Anonymous()
  {
    this.count++;
  }
}

public Action Counter()
{
  Locals locals = new Locals();
  locals.count = 0;
  Action counter = new Action(locals.Anonymous);
  return counter;
}

Как будет выглядеть лямбда, а также длинный код?

Ответы [ 4 ]

5 голосов
/ 24 января 2011

Если у вас есть причина для блокировки, то да, ничто не мешает вам поместить оператор lock в закрытие.

Например, вы можете сделать это:

public static Action<T> GetLockedAdd<T>(IList<T> list)
{
    var lockObj = new object();
    return x =>
    {
        lock (lockObj)
        {
            list.Add(x);
        }
    }
}

Как это выглядит с точки зрения сгенерированного компилятором кода? Спросите себя: что захватывается?

  • Местный object используется для блокировки.
  • Передано IList<T>.

Они будут захвачены как поля экземпляра в сгенерированном компилятором классе. Таким образом, результат будет выглядеть примерно так:

class LockedAdder<T>
{
    // This field serves the role of the lockObj variable; it will be
    // initialized when the type is instantiated.
    public object LockObj = new object();

    // This field serves as the list parameter; it will be set within
    // the method.
    public IList<T> List;

    // This is the method for the lambda.
    public void Add(T x)
    {
        lock (LockObj)
        {
            List.Add(x);
        }
    }
}

public static Action<T> GetLockedAdd<T>(IList<T> list)
{
    // Initializing the lockObj variable becomes equivalent to
    // instantiating the generated class.
    var lockedAdder = new LockedAdder<T> { List = list };

    // The lambda becomes a method call on the instance we have
    // just made.
    return new Action<T>(lockedAdder.Add);
}

Имеет ли это смысл?

2 голосов
/ 24 января 2011

Да, это возможно.

Просто убедитесь, что вы не изменяете экземпляр заблокированного объекта, иначе он будет бесполезен.

1 голос
/ 24 января 2011

У вас может быть такая функция:

static Func<int> GetIncrementer()
{
    object locker = new object();
    int i = 0;
    return () => { lock (locker) { return i++; } };
}

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

0 голосов
/ 27 июня 2018

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

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

Вот вспомогательная функция (в статическом классе):

public static class Locking
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [DebuggerNonUserCode, DebuggerStepThrough]
    public static T WithLock<T>(this object threadSync, Func<T> selector)
    {
        lock (threadSync)
        {
            return selector();
        }
    }
}

А вот как вы его используете:

private readonly object _threadSync = new object();
private int _myProperty;

public int MyProperty
    => _threadSync.WithLock(() => _myProperty);
...