Rx Task.Factory.StartNew запускает две задачи по выпуску .Net 3.5 - PullRequest
4 голосов
/ 07 декабря 2010

Я использую часть задач библиотеки Reactive Extensions в .Net 3.5

Работает в основном плавно, но в одном месте дважды вызывает одну и ту же задачу.

Звонок выглядит так:

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

Есть идеи? Это ошибка?

---- обновление

Я думаю, проблема в том, как c # выполняет закрытие в лямбдах. проблема не была в 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 );
}

так что у меня есть замыкание вокруг процессора, и я предполагаю, что проблема заключалась в том, что он перерабатывал один и тот же объект для лямбды и менял процессор.

---- обновление 2

Я убежден, что это проблема. я рефакторинг и отладка System.Threading.dll оба раза я создаю задачу, она создается с одним и тем же делегатом (с одинаковым ObjectID), и процессор изменяется в свойстве Target между итерациями. кто-нибудь знает хорошую работу вокруг?

---- обновление 3 это тоже работает (спасибо Джуда Химанго):

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

1 Ответ

5 голосов
/ 07 декабря 2010

Вы используете переменные цикла внутри лямбды? например,

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

См. Почему плохо использовать итерационную переменную в лямбда-выражении?

ОБНОВЛЕНИЕ Ноябрь 2012 : Новый компилятор C # 5 решает эту проблему, изменяя поведение таким образом, что фиксируется переменная цикла foreach, т.е. Эрик Липперт из команды C # пишет ,

Мы принимаем последние изменения. В C # 5 переменная цикла foreach будет логически внутри цикла, и поэтому замыкания будут закройте свежую копию переменной каждый раз. Цикл "для" будет не подлежит изменению.

...