Lambda expressions, captured variables and threading
I think in your first example., you mean
QueueUserWorkItem( () => SendMessage(message) );
In your second item, where does the parameter s
come from? I think you mean
QueueUserWorkItem( s => {SendMessage((string)s);} , message );
Now, these two both work equivalently, but
In the first case: the parameter
message
is copied from the scope ofthisDoStuff
method and storeddirectly in your lambda expressionitself, as a closure. The lambda haskeeps a copy ofmessage
.In the second case:
message
is sentto theQueue
, and the queue keepshold of it (along with the lambda),until the lambda is called. It ispassed at the time of running thelambda, to the lambda.
I would argue that the second case is more programmatically flexible, as it could theoretically allow you to later change the value of the message
parameter before the lambda is called. However the first method is easier to read and is more immune to side-effects. But in practice, both work.
For your example (a reference to an immutable string object) it makes absolutely no difference.
And in general, making a copy of a reference is not going to make much difference. It does matter when working with value types:
double value = 0.1; ThreadPool.QueueUserWorkItem( () => value = Process(value)); // use captured variable // -- OR -- ThreadPool.QueueUserWorkItem( () => { double val = value; // use explicit parameter val = Process(val); // value is not changed }); // -- OR -- ThreadPool.QueueUserWorkItem( (v) => { double val = (double)v; // explicit var for casting val = Process(val); // value is not changed }, value);
The first version is not thread-safe, the second and third might be.
The last version uses the object state
parameter which is a Fx2 (pre-LINQ) feature.
- If you want the anonymous functors reference the same state, capture a identifier from common context.
- If you want stateless lambdas (as I would recommend in a concurrent application) use local copies.