AK_
AK_

Reputation: 8099

Rx Task.Factory.StartNew starts two tasks on .Net 3.5 issue

I'm using the tasks part of the Reactive Extensions library on .Net 3.5

It works mostly smoothly but in one place it calls the same task twice.

The call looks like this:

Task.Factory.StartNew(
    () => Processor.ProcessMessage(incomingMessage),
    TaskCreationOptions.PreferFairness );

Any ideas? Is this a bug?

---- update

i think the problem is with the way c# does closure in lambdas. the problem wasn"t in the TPL, the same problem returned with the plain old thread pool.

and this solved it:

foreach (var Processor in processors)
{
 object[] crap = new object[2];
 crap[0] = Processor;
 crap[1] = incomingMessage;
 Task.Factory.StartNew(Magic, crap, TaskCreationOptions.PreferFairness);
}

public void Magic(object obj)
{
 object[] crap =(object[]) obj;
 ((IIncomingMessageProcessor)crap[0]).ProcessMessage((IncomingMessageBase)crap[1]);
}

the original source was:

foreach (var Processor in processors)
{
Task.Factory.StartNew(
    () => Processor.ProcessMessage(incomingMessage),
    TaskCreationOptions.PreferFairness );
}

so i have closure around Processor, and i guess the problem was it was recycling the same object for the lambda and swapping the Processor.

---- update 2

I'm convinced this is the problem. i refactored and debugged the System.Threading.dll both times i create the task, it is created with the same delegate(Same ObjectID) and the Processor changes in the Target property between iterations. anyone knows a good work around?

---- update 3 this works too(thanks Judah Himango):

foreach (var processor in processors)
{
 var crap = processor;
 Task.Factory.StartNew(() => crap.ProcessMessage(incomingMessage), TaskCreationOptions.PreferFairness);
}

Upvotes: 4

Views: 2350

Answers (1)

Judah Gabriel Himango
Judah Gabriel Himango

Reputation: 60021

Are you using loop variables inside a lambda? e.g.

foreach (var foo in blah)
{
   // You're capturing foo inside a lambda -- this probably won't do what you think it will!
   Task.Factory.StartNew(() => foo.Something()); 
}

See Why is it bad to use an iteration variable in a lambda expression?

UPDATE November 2012: The new C# 5 compiler addresses this area of confusion by changing the behavior such that the foreach loop variable is captured, i.e. the expected behavior. Eric Lippert of the C# team writes,

We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The "for" loop will not be changed.

Upvotes: 5

Related Questions