2012-06-04 3 views
1

Скажем, у меня многопоточное приложение, и я запускаю его с теми же входами. Достаточно ли для каждой загрузки и хранилища регистрировать записи для записи и записи данных? Я имею в виду из зарегистрированных загружаемых и хранимых адресов, если мы можем видеть, какой поток выполнял загрузку и какой поток выполнял это хранилище, мы можем обнаружить расы записи записи и записи-записи, заметив перекрывающиеся адреса. Или я чего-то не хватает?Достаточно ли этого, чтобы обнаружить условия гонки?

+0

не будет автоматически искажать результаты, потому что это изменяет поведение во время работы ?! – 0xC0000022L

+1

Если бы все было так просто, я уверен, что проблема была бы решена давно. Обнаружение - это одно; предотвращение - совсем другое. – duffymo

+0

duffymo: Я просто говорю об обнаружении в этот момент. – pythonic

ответ

5

Или я что-то упускаю?

Вы: : отсутствует. Как сказал Пубби, если вы видите прочитанное, то напишите в T1, а затем прочитайте, затем напишите в T2, вы не можете сказать ничего об отсутствии рас. Вам нужно знать о блокировках.

Возможно, вы захотите использовать такой инструмент, как Google ThreadSanitizer.

Update:

Но будет ли мой подход охватывает все расы или, по крайней мере, некоторые из рас?

Ваши комментарии здесь и по другим ответам показывают, что вы не понимаете, что такое гонка.

Ваш подход май выставить рас, да. Гарантируется, что не покрывает большинство из них (что сделает упражнение бесполезным).

+1

+1; но это ThreadSanitizer, не Thread Sanitizer. –

+0

Давай, парень предоставил ссылку. – duffymo

+0

@Employed Russian: Я в порядке с ложными срабатываниями, это признак гонки, даже если его нет. Но будет ли мой подход охватывать все расы или, по крайней мере, некоторые из рас? – pythonic

1

Наиболее очевидная вещь, которую вы узнаете, состоит в том, что существует несколько потоков, использующих одну и ту же память. Это не обязательно плохо само по себе.

Хорошее использование будет включать защиту с помощью семафоров, атомарного доступа и механизмов типа RCU или двойной буферизации.

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

  • условие гонки в основном стволовые от упорядочения вопросов - если какая-то задача пишет что-то в конце его исполнения, тогда как задача B необходимо это значение в его начало, вы должны убедиться, что чтение B происходит только после завершения A. Хорошим решением для этого являются семафоры, сигналы или подобное. Или, конечно, запустите его в том же потоке.
  • Истинное совместное использование означает, что два или более ядра активно считывают и записывают один и тот же адрес памяти. Это замедляет работу процессора, так как ему постоянно приходится отправлять какие-либо изменения в кеши других ядер (и, конечно, память). Ваш подход мог поймать это, но, вероятно, не выделить его.
  • Ложное совместное использование еще более сложное, чем реальное совместное использование: кеши процессора не работают ни на одном байте, а на «линиях кеша», которые содержат более одного значения. Если ядро ​​A продолжает забивать байт 0 строки, тогда как ядро ​​B продолжает записывать в байт 4, обновление кеша все равно остановит весь процессор.
+0

Я уже знаю эти вещи. Это не отвечает на мой вопрос. – pythonic

2

Вот простой пример из Википедии, что я слегка модифицирована:

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

  1. Целое число i = 0; (Память)
  2. Т1 считывает значение I из памяти в register1: приращение 0
  3. Т1 значение I в register1: (содержание register1) + 1 = 1
  4. T1 сохраняет значение register1 в памяти: 1
  5. Т2 считывает значение I из памяти в Register2: 1
  6. Т2 умножает значение I в Register2: (содержание REGISTER2) ​​* 2 = 2
  7. T2 сохраняет значение Register2 в памяти: 2
  8. Целое число i = 2; (память)

В случае, показанном выше, конечное значение i равно 2, как и ожидалось. Однако, если два потока выполняются одновременно без блокировки или синхронизации, результат операции может быть неправильным. Следующая альтернативная последовательность операций демонстрирует этот сценарий:

  1. Целое число i = 0; (Память)
  2. Т1 считывает значение I из памяти в register1: 0
  3. Т2 считывает значение I из памяти в Register2: приращение 0
  4. Т1 значение I в register1: (содержание register1) + 1 = 1
  5. Т2 умножает значение I в Register2: (содержание REGISTER2) ​​* 2 = 0
  6. T1 сохраняет значение register1 в памяти: 1
  7. T2 сохраняет значение Register2 в памяти: 0
  8. Целое число i = 0; (Память)

Окончательное значение I равно 0 вместо ожидаемого результата 2. Это происходит потому, что инкремент операция второго случая не является взаимоисключающей . Взаимоисключающие операции - это те, которые не могут быть прерваны при доступе к некоторому ресурсу, например, к памяти . В первом случае T1 не прерывался при доступе к переменной i, поэтому ее операция была взаимно исключающей.

Все эти операции являются атомарными. Состояние гонки происходит потому, что этот определенный порядок не имеет той же семантики, что и первая. Как вы доказываете, что семантика не совпадает с первой? Ну, вы знаете, что они разные для этого случая, но вам нужно доказать каждый возможный порядок, чтобы определить, что у вас нет условий гонки. Это очень трудная задача и имеет огромную сложность (возможно, NP-hard или требует AI-complete) и, следовательно, не может быть надежно проверена.

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

Если вы говорите об использовании последовательных операций чтения или записи для определения расы, а затем наблюдать это:

  1. Integer я = 0; (Память)
  2. Т2 считывает значение I из памяти в Register2: 0
  3. Т2 умножает значение I в Register2: (содержание REGISTER2) ​​* 2 = 0
  4. T2 сохраняет значение Register2 в памяти: 0
  5. Т1 считывает значение I из памяти в register1: приращение 0
  6. Т1 значение I в register1: (содержание register1) + 1 = 1
  7. T1 сохраняет значение register1 в памяти: 1
  8. Целое число i = 1; (Память)

Это имеет тот же шаблон для чтения/магазин как первый, но дает различные результаты.

+0

Вы, кажется, не поняли мой вопрос. Здесь, если мы обрабатываем каждую загрузку и сохранение потоков 1 и 2, мы действительно увидим, что T2 и T1 записываются в одну и ту же переменную и таким образом обнаруживают нарушение записи/записи. Что в этом примере так сложно? Я тоже в порядке с ложными срабатываниями. У меня скорее есть ложный позитив, чем состояние гонки не обнаружено. Я не требую последовательной записи для чтения, я думаю, что чтение/запись, даже разделенные инструкциями, прекрасное, чтобы сказать о своем состоянии гонки. Мы можем игнорировать части кода внутри замков. Я думаю, что я говорю о жизнеспособном подходе. – pythonic

+1

@ user1018562 Я не понимаю, как вы можете определить, какой код находится внутри замков (имейте в виду, что * код * не должен быть заблокирован, а вместо этого * данные *), поскольку это также требует решения проблемы с остановкой, и даже если вы возможно, все еще есть шансы на то, что произойдет гонка. – Pubby

Смежные вопросы