Изменение значения в итерации перед началом вызова - PullRequest
0 голосов
/ 01 июля 2010

В моем приложении есть следующий код.

MyEventHandler handler = null; //Declare the handler

foreach (string pname in group)
{
  handler = getHandler(pname); //Get the handler
  if(handler == null)
  {                        
      throw new KeyNotFoundException("No user " + pname + " could be found");
  }
  //invoke the handler
  handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
}

Итак, я получаю обработчик и вызываю BeginInvoke метод. Но перед вызовом BeginInvoke он переходит к следующей итерации и значение обработчика изменяется. Так что BeginInvoke подключается к этому новому обработчику.

Надеюсь, вы поняли мою точку зрения. Итак, как я могу устранить эту проблему? Я не хочу звонить спать после BeginInvoke, так как чувствую, что это потеря времени.

Есть идеи?

Update1 Я уверен, что объект обработчика изменяется перед вызовом BeginInvoke (). Я предполагаю, что BeginInvoke требуется некоторое время для создания отдельного потока для вызова другой функции.

Update2 Этот код находится в службе WCF, и клиенты вызывают функцию, которая, в свою очередь, использует эту функцию. У меня на сервере хранятся отдельные обработчики для каждого клиента. Служба WCF имеет дуплексный контракт с разделением сеансов для клиента. Я вижу, что после выполнения этой функции один и тот же пользователь вызывается дважды. Но я ставлю точку останова и отлаживаю ее (что дает BeginInvoke необходимое время для вызова функции), она работает «ОТЛИЧНО». Я очень уверен, что столкнулся с этой проблемой в потоке тоже, где я создаю несколько потоков в цикле. Если делегат потока имеет параметры a, b, c и если вы изменяете его в начале следующей итерации, происходит то же самое поведение. Я не знаю, как многие из вас сталкивались с этой проблемой раньше. Если я поставлю Sleep () или сделаю копию обработчика и вызову его с помощью copy, он будет работать.

Update3

Хорошо, я проверил это сейчас. Я просто добавил Thread.Sleep () следующим образом.

chatTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
Thread.Sleep(500);

и он работает как шарм. Есть мысли?

Обновление 4

Я создал образец потока, демонстрирующий проблему, и Я загрузил его сюда . Я надеюсь, что решение этой проблемы решит и мою проблему. Пожалуйста, проверьте образец.

Ответы [ 3 ]

5 голосов
/ 01 июля 2010

Хорошо, после вашего четвертого редактирования вы предоставите нам пример, который показывает проблему, конечно, но не проблему, о которой вы просите помощи о .

Что вы говорите в вопросе, так это:

  • Я использую BeginInvoke для переменной делегата, затем меняю переменную, и как-то мой делегат вызывается дважды

Что вы демонстрируете в размещенном коде, так это:

  • Я фиксирую переменную цикла в анонимном методе и каким-то образом использую неверное значение переменной

ЭТО НЕ ТО ЖЕ ПРОБЛЕМА!

Причиной неправильного поведения вашего размещенного кода является то, что данный код на самом деле выглядит так:

int i;
for (i = 0; i < 10; i++)
    ... create delegate, capture i, spawn thread

Здесь вы записываете одну и ту же переменную для всех потоков. Если поток не начнет выполняться до того, как цикл изменит переменную, то да, вы увидите «неправильное значение» для этой переменной.

Однако, если вы измените код следующим образом:

for (int i = 0; i < 10; i++)
{
    int j = i;
    ThreadStart threadStartObj = new ThreadStart(
        delegate { PrintValueThreadFunction(j, j); });
                                            ^
                                            |
                                            +-- use j instead of i here

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

Итак, вопрос остается. Это ваша проблема? Если так, то позор вам , в следующий раз не упростит задачу . Вы напрасно тратите время людей, прежде всего свое. Если бы вы опубликовали код, подобный приведенному выше, для начала у вас был бы ответ (или дублирующий вопрос, указывающий на существующие ответы, их достаточно) в течение пары минут.

Если это не та проблема, с которой вы столкнулись, у вас все еще есть проблема с обработчиком событий, как в исходном коде, который вызывается более одного раза, вернитесь и создайте лучший пример проекта.

1 голос
/ 01 июля 2010

Я не понимаю, почему это произошло - код, который вы разместили, не может воспроизвести описанное вами поведение. Вполне разумно, что вызов BeginInvoke может фактически не делать ничего сразу, и что следующая итерация может произойти до того, как вы действительно увидите, что этот вызов что-то делает - поскольку он будет поставлен в очередь для обработки рабочим потоком.

Это не означает, что вызывается другой обработчик - вызываемый обработчик захватывается, как только вызывается BeginInvoke, поэтому не имеет значения, будет ли локальная переменная изменяться впоследствии .

Кроме того - почему у вас здесь замок? Если несколько потоков не выполняют эту обработку одновременно в течение одного и того же перечислимого (в этом случае зачем вам это делать), я не вижу никакой причины, по которой вы могли бы заблокировать.

Я бы также сказал, что если вы судите об этом поведении по тому, что видите в отладчике, то вам не стоит беспокоиться - вы получите «интересные» результаты от отладчика, выполнив это и с несколькими потоками. в миксе важно переключать потоки в окне отладчика Threads.

Вопрос в том, действительно ли ваша программа делает то, что вы ожидаете? Если так, но вы видите это странное поведение во время отладки - тогда это совершенно нормально.

Как отмечалось в нескольких комментариях, код, который вы разместили, не может быть именно тем, что создает проблему. Если, например, «обработчик» - это локальная переменная, совместно используемая несколькими потоками, которые затем выполняют эту итерацию, тогда да, вы можете получить что-то вроде этого. Но переменная, локальная для метода, может быть изменена (и действительно прочитана) только тем потоком, который в данный момент в этом методе ; единственное исключение из этого правила заключается в том, что ссылка handler передается другому потоковому методу как ref.

0 голосов
/ 01 июля 2010

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

...