Lambda expressions, captured variables and threads

I know that .NET lambda expressions can capture external variables. However, I have seen many times that variables are passed explicitly to the lambda expression as a parameter, and the .NET library also seems to support this (e.g. ThreadPool.QueueUserWorkItem).

My question is, what are the limitations of these captures? What about lambdas that actually execute on a different thread than the one that was created (e.g. ThreadPool.QueueUserWorkItem or Thread) or lambas that act as callbacks (i.e. called later)?

Generally, when should I rely on captured variables and when to use explicit parameters? For example:

public void DoStuff() { string message = GetMessage(); ThreadPool.QueueUserWorkItem(s => SendMessage(message)); // use captured variable // -- OR -- ThreadPool.QueueUserWorkItem(s => { string msg = (string)s; SendMessage(msg); }, message); // use explicit parameter } 

Thanks!

Update : fixed the second example ThreadPool.QueueUserWorkItem.

+7
multithreading c # lambda
source share
3 answers

I think in your first example. do you mean

 QueueUserWorkItem( () => SendMessage(message) ); 

In your second paragraph, where does the s parameter come from? I think you mean

 QueueUserWorkItem( s => {SendMessage((string)s);} , message ); 

Now these two both work the same, but

  • In the first case: the message parameter is copied from the scope of this DoStuff method and stored directly in the lambda expression as a closure. Lambda stores a copy of message .

  • In the second case: message sent to Queue , and the queue is saved (along with the lambda) until the lambda appears. it passed during the launch of lambda, lambda.

I would say that the second case is more programmatically flexible, since it theoretically allows you to later change the value of the message parameter before calling the lambda. However, the first method is easier to read and more protected from side effects. But in practice, both work.

+9
source share

In your example (reference to an immutable string object), this is absolutely irrelevant.

In general, a copy of the link will not matter much. This is important when working with type values:

  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 may be.

The latest version uses the object state parameter, which is a Fx2 (pre-LINQ) function.

+5
source share
  • If you want anonymous functors to refer to the same state, write down the identifier from the general context.
  • If you want stateless lambdas (as I would recommend in a parallel application), use local copies.
+1
source share

All Articles