3

I am using a class that manages a pool of threads to run actions. Originally it was coded to take an Action (with no parameter) and I was calling it like this:

void LoadTasks()
{
   string param;

   // some code loops and changes param
   {
      threadPool.EnqueueTask(() => SomeMethod(param));
   }
}

As the threads ran some were fine, but occasionally the param variable was not what I expected ... it was a "newer" value and not what I intended to send to the method.

Changing the thread pool to accept Action<Object> and calling without a lambda -- like this threadPool.EnqueueTask(SomeMethod, param) -- worked around my problem.

I see quite a few questions about C# lambdas about thread-safety. For example, an accepted answer of lambdas are much less likely to be thread safe than you would expect. I'm finding other questions and answers about lambdas/closures/scoping to be confusing. So I am looking for an explanation of lambdas and variable scope, ideally relating to the problem in my example.

Community
  • 1
  • 1
jltrem
  • 12,124
  • 4
  • 40
  • 50
  • 1
    This question and my answer might be of help to you: http://stackoverflow.com/questions/8898925/is-there-a-reason-for-cs-reuse-of-the-variable-in-a-foreach – Eric Lippert Jul 10 '13 at 15:59

1 Answers1

2

So the problem is that you're closing over variables that you don't mean to. The easy fix in most all cases is to create a new local variable, copy the variable you were once closing over, and then close over that instead.

So instead of:

for(int i = 0; i < number; i++)
{
    threadPool.EnqueueTask(() => SomeMethod(someList[i]));
}

You can just do:

for(int i = 0; i < number; i++)
{
    int copy = i;
    threadPool.EnqueueTask(() => SomeMethod(someList[copy]));
}

Now each lambda is closing over it's own variable, rather than having all of them close over the same one variable.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • Please correct me if I am wrong: I understand that the variables in a lambda function are evaluated when that function is called. So in my case the 'param' string was still in scope in one thread, and when the lambda executed in another thread it would use and whatever value 'param' had at that moment. So in response to your statement "now each lambda is closing over it's own variable", I ask what makes a variable "owned" by a lambda? Is it owned if it is not in scope anywhere else? – jltrem Jul 10 '13 at 19:34
  • @jltrem The lambda doesn't "own" it, it's simply a question of whether two lambdas are closing over the same one variable, or whether there are two variables so that each variable is only ever closed over by one lambda. In some cases it's *desirable* for multiple lambdas to close over one variable, you have indicated that, in this case, you don't want that, so the way to avoid it is to create new variables. – Servy Jul 10 '13 at 19:56
  • Really my confusion was not understanding the concept of _closure_. I did not fully comprehend the connection between 1) lambda expressions capture variables, and 2) captured variables are not evaluated until the code is invoked. It just hadn't sunk in. Rookie mistake. Thanks for setting me in the right direction. – jltrem Jul 11 '13 at 15:28