我正在使用.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);
}
答案 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”循环将 不要改变。