Race condition и Data Race

Шведская студия SimBin

— одна из немногих организаций планеты Земля, продолжающих создавать серьезные автосимуляторы. За свою короткую историю эта компания ни разу не выпустила двух похожих игр подряд.
Race: The WTCC Game
подтверждает эту приятную тенденцию — от вышедшей осенью
GTR 2: FIA GT Racing Game
она отличается самым радикальным образом. И это несмотря на смехотворный временной пробел между двумя релизами. Скажем больше — если скандинавы продолжат в том же духе, то поклонникам хардкорных автогонок точно не придется скучать.

On the Road Again

Борьба нос к носу в WTCC — дело обычное. Машины-то почти одинаковые.

На этот раз в центре внимания — не засветившийся до сего момента в играх чемпионат World Touring Car Championship (WTCC). Меж тем WTCC в табели о рангах Международной федерации автоспорта (FIA) уступает разве что «Формуле-1» и WRC. На старт здесь выходят максимально близкие к серийным автомобили, легко узнаваемые легковушки вроде BMW 320, Alfa Romeo 156, Seat Leon, Honda Accord, Chevrolet Lacetti и им подобные. Технический регламент весьма демократичен, так как в рамках национальных первенств на подобных машинах (категория Super 2000) гоняют почти по всему миру. Так, правила предписывают наличие двигателя рабочим объемом не более двух литров и мощностью не более 300 лошадиных сил, а также привод только на одну ось. Понятно, что подобные агрегаты не могут развивать запредельных скоростей (как это делают болиды, участвующие в FIA GT), но у них кроме простоты и доступности есть свои преимущества. Благодаря сравнительно небольшой разнице в скорости среди участников гонки получаются крайне напряженными и богатыми на обгоны и столкновения. Именно такие заезды идеально описывает англоязычный термин close racing

.

Гоночный уик-энд в WTCC состоит не из одной, как обычно, а из двух гонок, причем на втором старте восьмерка лидеров перетасовывается в обратном порядке. Если после окончания первого заезда ваша машина незначительно повреждена, то ее тут же на месте отремонтируют. А вот если вы пересекли финиш на трех колесах или совсем не доехали до него, то и на старт второго дня вас не пустят. Поскольку очки начисляются за оба зачетных заезда, то стабильные финиши в очковой зоне могут принести больше дивидендов, чем редкие, но яркие победы.

В RACE представлены все автомобили сезона-2006, включая новую «трешку» BMW.

Но и это далеко не все: дабы еще более обострить борьбу, организаторы награждают особо шустрые экипажи лишними килограммами балласта, который должен замедлять явных лидеров. И наоборот, отстающим могут сделать поблажку в виде уменьшения гандикапа. В игре все вышеозначенные тонкости реализованы и работают, но только в режиме чемпионата. Так что если вы, скажем, закончите первый этап в Монце на подиуме, то к следующей гонке в Маньи-Куре вашей машине добавят несколько десятков килограмм лишнего веса. Понятно, что скорость эти лишние килограммы не увеличат, и выиграть на загруженной машине будет заметно сложнее. Благодаря таким вот необычным правилам проходить здешний чемпионат невероятно интересно. Гонки, в отличие от GT, здесь сравнительно короткие (правда, и сохраняться по ходу заезда теперь нельзя), регулярные пит-стопы отсутствуют, а балом правит постоянная борьба за позицию — большие отрывы, как правило, просто не успевают образоваться.

Каждый из десяти этапов в календаре чемпионата здесь представляет собой уникальный челлендж: к примеру, присутствует невероятно грязный асфальт мексиканской трассы Пуэбла, ощущения от которого схожи с зимней ездой на лысых покрышках. Кстати о трассах: среди них имеется по крайней мере две настоящие жемчужины. Во-первых, это Брендс-Хэтч, один из самых старых и интересных британских автодромов, а во-вторых, Макао, трасса, проложенная по улицам бывшей португальской колонии. Гонки по этой изобилующей опасными поворотами и перепадами высот дороге сродни езде по горному серпантину, только в случае ошибки вас подстерегает не пропасть, а металлический рельс-отбойник. К тому же ширины дорожного полотна местами недостаточно даже для двух машин, что очень и очень затрудняет обгоны.

Ездить в дождь теперь и интереснее и красивее, благодаря работающим «дворникам».

Местные болиды, конечно, вряд ли приведут в восторг пилотов, только что пересевших с GTR 2, — их разгонная и тормозная динамика, как и поведение на дороге, поначалу кажется слишком вялой. Но тут важно понимать, что кажущаяся простота управления — это особенность конструкции авто, многие узлы которого аналогичны серийным моделям. То есть в данном конкретном случае «сложнее

» вовсе не означает «
реалистичнее
». Тем более что, когда на трассе завязывается борьба, вся эта аналитика моментально улетучивается, уступая место драйву.

Удовольствие в разумных пределах теперь смогут получить не только обладатели рулей, но и пилоты, предпочитающие управление с клавиатуры, — лишь бы руки из нужного места росли. Ну и конечно, неплохо бы следовать советам («перед поворотами нужно тормозить!

»), которые SimBin заботливо встроили в игру специально для новичков.

Кроме чемпионата, различных вариантов практик и одиночных заездов в игре имеется весьма любопытный режим под название Driver Duel. Тут вашим соперником будет выступать время поул-позишн реальных гонщиков WTCC, установленное в сезоне 2006 года. Те виртуальные пилоты, которые смогут превзойти результат профи, могут смело предлагать свои услуги какой-нибудь из гоночных «конюшен», ибо проехать круг в том же Брендс-Хэтч за 1 минуту 34 секунды пока представляется невозможным.

Race On: Обзор

SimBin

– компания весьма известная… в определенных кругах. Делает она автосимуляторы. Те, где настоящая физика, реалистичная модель управления и не выдуманные, а реальные гоночные треки. Массового пользователя «страшная» достоверность, конечно, отталкивает, зато среди ценителей жанра игры GTR 2 и GT Legends не просто любимы, а считаются чуть ли не эталоном.
Race On
– дополнение к очередному продолжению серии. Race 07 – как бы вторая часть Race, а On – всего лишь расширение, которое добавляет к и так уже напичканной автомобилями и треками игре еще кое-что. Как-то запутанно, не находите? Все в порядке. Разработчики специально запутывают покупателя, но вот зачем?

Настраивается почти все, но, в отличие от NFS:Shift, здесь опции скомпонованы очень компактно.
Всем давно известная схема
Чтобы сделать простое продолжение-дополнение к любой игре – не нужно много думать. Есть же стандартная программа. К стратегии можно добавить пару новых видов техники и несколько карт. Шутер обойдется тремя свежими пушками и одним уровнем, а ролевые боевики по накатанной колее получают пятый

город и двух новых героев. Сделать дополнение к гонке – легче всего. Ставим несколько машин в гараж, рисуем новый трек – и вот уже можно продавать «новинку».
Race On
– как раз такой случай.
Simbin
взяли Race 07, добавили туда STCC, приправили американскими автомобилями и парой трасс на территории США и… все, собственно. Класс GT отсутствует, будто и не было GTR Evolution. Зато есть легкие спорткары Caterham из одноименного дополнения… В общем, что же нового-то?

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

штук. Это Cadillac CTS-V, Chevrolet Camaro, Dodge Challenger SRT8, Dodge Charger SRT8 и маловесные болиды IFM, которые внешне похожи на автомобили Formula-1. Треков новых всего два – это Laguna Seca и Road America. Все остальное так или иначе присутствовало в прошлых дополнениях. Например, большая часть маршрутов и соревнований остались от Official WTCC Game.

На этом список нововведений заканчивается… Удивительно? Да нет, в принципе. Чем у нас отличилась
SimBin
в этом году?
Race Pro
в феврале. Исключительно для Xbox 360, поэтому нам не интересно. Что потом? Volvo: The Game, которую и игрой-то назвать нельзя. Теперь вот Race On – типичный content pack, бездушный и серый. Кризис, что ли, опять виноват?

У SimBin

, видимо, совсем плохи дела, коли они без зазрения совести родили
Race On
. Движок – все тот же. С шейдерами не дружит, поэтому мир хоть и использует прекрасные текстуры высокого разрешения, все одно будто картонный. Заботливо созданные модели автомобилей не трогают – в NFS: Shift их больше и выглядят они лучше. Пачка новых американских чемпионатов – да кому оно надо? Разве что американцам. Да и подборка автомобилей, кстати, опять наводит на мысли о «скрытой» рекламе. Финансовая обстановка, вроде, налаживается, чего не скажешь про автопром США.

Доброго слова заслуживает только физика – игра все такой же честный симулятор. Вы можете рулить мышкой и попадать в повороты, но без полноценных педалей газа, тормоза и желательно сцепления поиграть так, как задумано

разработчиком – не выйдет.

есть добродетели – это все же симулятор. Но вот кому он нужен, на что надеются разработчики? Здесь нечем заняться казуальному игроку. Тому, кто пришел просто прокатиться пару трасс, будет скучно – нет динамики и увлекательности аркады. Сюжет, достижения, экономическая модель и развесистое дерево улучшений – всего этого тут нет. Ничего, кроме самого процесса вождения… Очевидно, что с таким классным и милым NFS: Shift игре спорить просто нечем, хотя схватка, заметьте, уже не на аркадном поле. Даже в нише чистокровных симуляторов
Race On
провалился. Слишком незначительные нововведения он предлагает, чтобы платить еще раз за одну и ту же игру.

Плюсы:

в основе лежит полноценный автосимулятор.
Минусы:
устаревший технический уровень; почти полное отсутствие музыки; мало нововведений.

Вне трассы

На узких улочках Макао обгонять невероятно сложно. Потому езда «паровозиком» тут нормальное явление.

Технически RACE выполнена стандартно, без сюрпризов: все машины и трассы сезона 2006 года в наличии и доступны с самого начала. Кроме них, есть также вспомогательные серии — кубок «Мини» и парочка машин 1987 сезона: BMW M3 и Alfa Romeo 75. Все автомобили делятся на две разновидности: с задним (BMW) и передним (все остальные) приводом. Вопрос выбора здесь — исключительно дело вкуса, кому как больше нравится. Нужно помнить, что приемы укрощения передне- и заднеприводных машин серьезно различаются: те же BMW требуют очень аккуратной работы с педалью газа, иначе в сложной ситуации задний мост обязательно попытается обогнать передний. Переднеприводники, в свою очередь, при добавлении газа, наоборот, стремятся на внешнюю сторону поворота.

Другое дело, что имеющихся машин и трасс довольно скоро начинает не хватать, но разработчики обещают восполнить этот пробел выпуском патчей и аддонов. Нечто подобное затевалось SimBin еще для дебютной GTR (она умела ходить в интернет и скачивать различные обновления на выбор). Учитывая, что RACE распространяется посредством Steam (даже коробочная версия требует обязательной активации), реализовать подобное несложно — было бы желание.

В техническом плане RACE — прямая наследница GTR 2, что совсем не удивительно. Тот же движок, та же картинка, даже многие трассы, и те перекочевали из предшественника. В итоге картинка получается пристойная, однако с пресловутым некст-геном, конечно, ничего общего.

Единственное новшество здесь — это… работающие стеклоочистители (на официальном форуме игры уже вовсю ехидничают по этому поводу). Как же их, оказывается, не хватало в предыдущих играх SimBin! А теперь вот совсем другое дело: стоит нажать кнопку, и щетки несколькими взмахами очистят лобовое стекло от налипшей грязи и насекомых. Наблюдать за этим процессом — одно удовольствие (разумеется, если вы предпочитаете ездить с видом из кабины). Несколько удивляют разве что модели машин, которые отчего-то похудели в полигонах. Возможно, ради того, чтобы устранить один из главных недостатков GTR 2 — совершенно неоправданные тормоза. Радикальный способ, ничего не скажешь — сродни лечению головной боли путем ампутации. Требования к железу для комфортной игры по-прежнему остаются немаленькие, и тут надо идти разработчикам навстречу, потому как в автогонках количество кадров в секунду — показатель критический.

Race condition и Data Race

by Defog Tech
Race condition и data race — две разные проблемы многопоточности, которые часто путают. Попробуем разобраться.

Существует много формулировок определения:

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

Race condition — ошибка проектирования многопоточной системы или приложения, при которой работа системы или приложения зависит от того, в каком порядке выполняются части кода.

Race condition — это нежелательная ситуация, которая возникает, когда устройство или система пытается выполнить две или более операций одновременно, но из-за природы устройства или системы, операции должны выполняться в правильной последовательности, чтобы быть выполненными правильно.

Race condition — это недостаток, связанный с синхронизацией или упорядочением событий, что приводит к ошибочному поведению программы.

Но мне нравиться наиболее короткое и простое:

Race condition — это недостаток, возникающий, когда время или порядок событий влияют на правильность программы.

Важно, что Race condition — это семантическая ошибка.

В проектирование электронных схем есть похожая проблема:

Состязание сигналов — явление в цифровых устройствах несоответствия работы данного устройства с заданным алгоритмом работы по причине возникновения переходных процессов в реальной аппаратуре.

Рассмотрим пример, где результат не определен:

go func() { fmt.Printf(«A->»)}()go func() { fmt.Printf(«B»)}()

Если запустить такой код много раз, то можно увидеть примерно такое:

A->BA->BA->BA->B
BA->A->B
Результат выполнения кода зависит от порядка выполнения горутин. Это типичная ошибка race condition. Ситуации могут быть гораздо сложней и не очевидней.

Учитывая, что race condition семантическая ошибка, нет общего способа который может отличить правильное и неправильное поведение программы в общем случае.

Помочь могут хорошие практики и проверенные паттерны.

Еще один пример:

x := 0for { go func() {
x++ }() go func() {if x%2 == 0 { time.Sleep(1 * time.Millisecond) fmt.Println(x) } }()}
В результате на консоле получим четные и нечетные числа, а расчитывали увидеть только четные.

Проблемы с доступом к общим ресурсам проще обнаружить автоматически и решаются они обычно с помощью синхронизации:

var mu sync.Mutexx := 0for{ go func() {
mu.Lock() x++mu.Unlock() }() go func() {mu.Lock() if x%2 == 0 { time.Sleep(1 * time.Millisecond) fmt.Println(x) }mu.Unlock() }()}
или локальной копией:

x := 0for i := 0; i < 1000; i++ { go func() { x++ }() go func() {
y := x if y%2 == 0 { time.Sleep(1 * time.Millisecond) fmt.Println(y) } }()}

Data race это состояние когда разные потоки обращаются к одной ячейке памяти без какой-либо синхронизации и как минимум один из потоков осуществляет запись.

Пример с балансом на счету:

type account struct { balance int}func deposit(acc *account, amount int) {
acc.balance += amount}
Запускаем в разных горутинах:

acc := account{balance:
0}var wg sync.WaitGroupfor i := 0; i < 1000; i++ { wg.Add(1) go func(n int) {deposit(&acc, 1) wg.Done() }(i)}wg.Wait()fmt.Printf(«balance=%d\n», acc.balance)
Изначально баланс равен 0, депозитим 1000 раз по 1. Ожидаем баланс равный 1000, но результат другой:

balance=
876
Потеряли много денег.

Причина в том, что операция acc.balance += amount не атомарная. Она может разложиться на 3:

tmp := acc.balancetmp = tmp + amountacc.balance = tmp

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

Например, у нас 2 параллельных потока выполнения, каждый должен прибавить к балансу по 1:

tmp := acc.balance // 100 || tmp := acc.balance // 100tmp = tmp + amount // 101 || tmp = tmp + amount // 101acc.balance = tmp // 101 || acc.balance = tmp // 101

Ожидали получить баланс=102, а получили = 101.

У Data Race есть точное определение, которое не обязательно связано с корректностью, и поэтому их можно обнаружить. Существует множество разновидностей детекторов гонки данных (статическое/динамическое обнаружение, обнаружение на основе блокировок, обнаружение основанное на предшествующих событий, обнаружение гибридного data race).

У Go есть хороший Data Race Detector с помощью которого такие ошибки можно обнаружить.

Решается проблема с помощью синхронизации:

var mu sync.Mutexfunc deposit(acc *account, amount int) {
mu.Lock() acc.balance += amountmu.Unlock()}
Иногда более эффективным решением будет использовать пакет atomic.

func deposit(acc *account, amount int64) {
atomic.AddInt64(&acc.balance, amount)}
Функция для перевода средств с одного счета на другой:

func transfer1(accFrom, accTo *account, amount int) error { if accFrom.balance < amount { return fmt.Errorf(«accFrom.balance } accTo.balance += amount accFrom.balance -= amount return nil}

На одном счету у нас будет 1000, а на другом 0. Переводим по 1 в 1000 горутинах и ожидаем, что все деньги из одного счета перетекут в другой:
accFrom := account{balance: 1000}accTo := account{balance: 0}var wg sync.WaitGroupfor i := 0; i < 1000; i++ { wg.Add(1) go func(n int) { err := transfer1(&accFrom, &accTo, 1) if err != nil { fmt.Printf(«error for n=%d\n», n) } wg.Done() }(i)}wg.Wait()fmt.Printf(«accFrom.balance=%d\naccTo.balance=%d\n», accFrom.balance, accTo.balance)
Но результат может быть таким:

accFrom.balance=
84accTo.balance=915
Если запустить цикл на большее кол-во операций, то можно получить еще интересней:

accFrom.balance=
0accTo.balance=997
При вызове из нескольких потоков без внешней синхронизации эта функция допускает как dara race (несколько потоков могут одновременно пытаться обновить баланс счета), так и race condition (в параллельном контексте это приведет к потере денег).

Для решения можно применить синхронизацию и локальную копию. Общая логика может быть не такой линейной и в итоге код может выглядит например так:

func transfer2(accFrom, accTo *account, amount int) error {
mu.Lock()bal := accFrom.balancemu.Unlock() if bal < amount { return fmt.Errorf(«accFrom.balance } mu.Lock() accTo.balance += amount mu.Unlock()mu.Lock() accFrom.balance -= amount mu.Unlock() return nil}
У нас синхронизированы все участки с записью и чтением, у нас есть локальная копия, Race Detector больше не ругается на код. Запускаем 1000 операций и получаем верный результат:

accFrom.balance=
0accTo.balance=1000
Но что если горутин будет 10к:

accFrom.balance=
-15accTo.balance=1015
Мы решили проблему data race, но race condition остался. В данном случае можно сделать блокировку на всю логику перевода средств, но это не всегда возможно.

Решив Data Race через синхронизацию доступа к памяти (блокировки) не всегда решается race condition и logical correctness.

Код примеров github.

На сегодня все. Спасибо!

Рейтинг
( 2 оценки, среднее 4.5 из 5 )
Понравилась статья? Поделиться с друзьями: