Rx Task.Factory.StartNew在.Net 3.5问题上启动两个任务

时间:2010-12-07 15:23:39

标签: c# .net multithreading system.reactive

我正在使用.Net 3.5上的Reactive Extensions库的任务部分

它的工作原理很顺利,但在一个地方它会调用相同的任务两次。

电话看起来像这样:

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

有什么想法吗?这是一个错误吗?

----更新

我认为问题在于c#在lambdas中关闭的方式。 问题不在TPL中,与普通旧线程池一起返回同样的问题。

这解决了它:

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]);
}

原始来源是:

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

所以我关闭了处理器,我想问题是它为lambda回收相同的对象并交换处理器。

----更新2

我确信这是问题所在。我重构并调试了System.Threading.dll 在我创建任务的两次中,它都是使用相同的委托(Same ObjectID)创建的,并且处理器在迭代之间的Target属性中更改。 谁知道一个好的工作?

----更新3 这也有效(感谢Judah Himango):

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

1 个答案:

答案 0 :(得分:5)

你在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()); 
}

请参阅Why is it bad to use an iteration variable in a lambda expression?

2012年11月更新:新的C#5编译器通过更改捕获foreach循环变量的行为(即预期行为)来解决这一混乱领域。 C#团队的Eric Lippert writes

  

我们正在进行重大改变。在C#5中,a的循环变量   foreach将在逻辑上位于循环内,因此闭包将会   每次关闭变量的新副本。 “for”循环将   不要改变。