开发者

Possible to Lock() within a Closure? What does that look like in Lambdas and code output?

开发者 https://www.devze.com 2023-02-06 03:15 出处:网络
I was reading this question, and read this response This is actually a fantastic feature. This lets you have a closure that

I was reading this question, and read this response

This is actually a fantastic feature. This lets you have a closure that accesses something normally hidden, say, a private class variable, and let it manipulate it in a controlled way as a response to something like an 开发者_Go百科 event.

You can simulate what you want quite easily by creating a local copy of the variable, and using that.

Would we need to implement Lock() in this situation?

What would that look like?

According to Eric Lippert a compiler makes code look like 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;
}

What does the Lambda would look like, as well as the long-form code?


If you have a reason to lock, then yes, there's nothing stopping you from putting a lock statement in a closure.

For example, you could do this:

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

What does this look like, in terms of compiler-generated code? Ask yourself: what is captured?

  • A local object used for locking.
  • The IList<T> passed in.

These will be captured as instance fields in a compiler-generated class. So the result will look something like this:

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);
}

Does that make sense?


Yes it is possible.

Just make sure you do not mutate the locked object instance, else it will be useless.


You could have a function like this:

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

When you call it, it will return a function that increments an internal counter in a thread-safe manner. Although not the best way to implement such a function, it does demonstrate a lock inside of a closure.


I came across this on my internet travels and I know it's a very old question, but I thought I'd propose an alternative answer to it.

It is possible to lock inside a lambda with the help of a wrapper function, which allows a relatively elegant syntax.

Here's the helper function (in a static class):

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

And here's how you use it:

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

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

精彩评论

暂无评论...
验证码 换一张
取 消