Приведет ли это к состоянию гонки в событийно-ориентированном программировании?

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

// Following is the event-loop per-se
Controller {
    if (...) {
       SendMessage(currentTime() + 5, i,j)
       SendMessage(currentTime() + 5, i,k)
    }
    print currentTime(), msgsRcvd
    Schedule(currentTime()+1, Controller)
}

// The following function is called when an 
// agent receives a message
Receive(Agent agent) {
    if (...) {
       msgsRcvd++ // <-- this is a global variable
    }
}

Насколько я понимаю, в currentTime() + 5 оба агента получают сообщение в одно и то же время, потому что оба события происходят в одно и то же логическое время, поэтому я должен видеть, что количество сообщений равно 2? Или я увижу, что происходит какое-то странное состояние гонки, и значение зависит от планировщика (т. Е. Это может закончиться печатью 1 или 2)? Какие-либо предложения?


person Legend    schedule 04.10.2011    source источник


Ответы (3)


Ответ зависит от реализации вашего транспорта событий и в этом смысле не зависит от языка.

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

Если ваша очередь событий имеет некоторый интеллект, который пытается консолидировать события на основе метки времени, вы увидите только одно событие в агенте-получателе. Я не знаю об общей системе, которая делает это (хотя некоторые системы пользовательского интерфейса могут, например, объединять два быстрых щелчка мыши в двойной щелчок... но это специфическое поведение конкретной системы событий, а не независимое от языка/платформы).

person Eric J.    schedule 04.10.2011
comment
+1 У меня сложилось впечатление, что это не зависит от языка, но спасибо за разъяснение. Я пересмотрел свои теги, чтобы указать, что именно я использую. - person Legend; 05.10.2011

Нет, хотя код агента вызывает большие подозрения и выглядит опасным, я не вижу, чтобы в данном случае он создавал состояние гонки: msgsRcvd всегда должен заканчиваться правильный итог. Даже если планировщик прерывает агента1 непосредственно перед приращением, мне кажется, что управление всегда будет возвращаться, чтобы завершить приращение. Если контроллер получит управление, то может случиться так, что он сообщит о неточном содержимом MsgsRcvd, ну и что? MsgsRcvd быстро возвращается в фазу.

Это, конечно, пугающий кусок кода. Когда я смотрю на такой код, я всегда хочу переместить приращение MsgsRcvd в контроллер, предоставив там функцию, которая будет выполнять приращение. Но в данном случае от этого мне стало бы только лучше; это не изменит логику и не решит «проблему» (если она есть) временной неточности MsgsRcvd.

person Pete Wilson    schedule 04.10.2011
comment
+1 за предложение о перемещении приращения. Да. Я собирался реализовать setter/getter в классе Controller, но заметил, что это не изменит логику. В любом случае, я сделаю это изменение, потому что оно просто сделает код чище. - person Legend; 05.10.2011
comment
Хорошая идея. Тогда я буду чувствовать себя гораздо лучше :-) - person Pete Wilson; 05.10.2011

Первоначально собирался упомянуть, что не существует независимого от языка/платформы способа ответить на этот вопрос, но Эрик Дж. прикрыл это.

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

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

person Chuu    schedule 04.10.2011
comment
+1 Спасибо за ваше время. Мне придется покопаться в движке моделирования ns-3, чтобы определить, как это делается. Возможно, я размещу вопрос на их доске объявлений. - person Legend; 05.10.2011
comment
@Chuu: Это хорошее наблюдение, если есть несколько потоков, получающих события. То же самое верно для многих языков, включая Java и C#, причина в том, что оператор приращения фактически представляет собой три дискретные операции (загрузить значение в регистр, увеличить этот регистр, поместить значение обратно в память). - person Eric J.; 05.10.2011