Possible to Lock() within a Closure? What does that look like in Lambdas and code output?
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);
精彩评论