При написании многопоточных приложений одной из наиболее распространенных проблем являются условия гонки.
Мои вопросы к сообществу таковы:
Что такое состояние гонки? Как вы их обнаруживаете? Как вы их обрабатываете? И наконец, как предотвратить их возникновение?
Состояние гонки возникает, когда два или более потока имеют доступ к общим данным и пытаются изменить их в одно и то же время. Поскольку алгоритм планирования потоков может менять потоки местами в любое время, вы не знаете, в каком порядке потоки будут пытаться получить доступ к общим данным. Поэтому результат изменения данных зависит от алгоритма планирования потоков, т.е. оба потока "гоняются" за доступ/изменение данных.
Проблемы часто возникают, когда один поток выполняет "проверку, затем действие" (например, "проверить", если значение равно X, затем "действовать", чтобы сделать что-то, что зависит от значения X), а другой поток делает что-то со значением в промежутке между "проверкой" и "действием". Например:
if (x == 5) // The "Check"
{
y = x * 2; // The "Act"
// If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
// y will not be equal to 10.
}
Дело в том, что y может быть 10, а может быть чем угодно, в зависимости от того, изменил ли другой поток значение x в промежутке между проверкой и действием. У вас нет реального способа узнать это.
Чтобы предотвратить возникновение ситуаций гонки, вы обычно накладываете блокировку на общие данные, чтобы гарантировать, что только один поток может получить доступ к данным одновременно. Это означает примерно следующее:
// Obtain lock for x
if (x == 5)
{
y = x * 2; // Now, nothing can change x until the lock is released.
// Therefore y = 10
}
// release lock for x
Возьмите этот пример:
<! - язык: c - >
for ( int i = 0; i < 10000000; i++ )
{
x = x + 1;
}
Пример:
< pre> Нить 1: читает x, стоимость равняется 7 Нить 1: добавьте 1 к x, стоимость равняется теперь 8 Нить 2: читает x, < b> стоимость 7 Нить 1: магазины 8 в x Нить 2: добавляет 1 к x, стоимость равняется теперь 8 Нить 2: < b> магазины 8 в xУсловий гонки можно избежать, используя своего рода < b> locking механизм перед кодексом, который получает доступ к общему ресурсу:
<! - язык: c - >
for ( int i = 0; i < 10000000; i++ )
{
//lock x
x = x + 1;
//unlock x
}
Что такое Условие Гонки?
Вы планируете ходить на фильм в 17:00. Вы справляетесь о доступности билетов в 16:00. Представитель говорит, что они доступны. Вы расслабляете и добираетесь до билетной кассы за 5 минут до шоу. I' m уверенный Вы можете предположить то, что происходит: it' s аншлаг. Проблема здесь была в продолжительности между проверкой и действием. Вы спросили в 4 и действовали в 5. Тем временем кто-то еще захватил билеты. That' s условие гонки - конкретно " проверьте тогда act\U 0026\quot; сценарий условий гонки.
Как Вы обнаруживаете их?
Религиозный кодовый обзор, многопоточные модульные тесты. Нет никакого короткого пути. Есть немногие плагин Затмения, появляющийся на этом, но ничем стабильном все же.
Как Вы обращаетесь и предотвращаете их?
Лучшая вещь состояла бы в том, чтобы создать побочный эффект бесплатные и не имеющие гражданства функции, использовать immutables как можно больше. Но это не всегда возможно. Так используя java.util.concurrent.atomic, параллельные структуры данных, надлежащая синхронизация и основанный на актере параллелизм помогут.
Лучший ресурс для параллелизма - JCIP. Вы можете также получить еще немного детали о вышеупомянутом объяснении здесь.
Есть важное техническое различие между условиями гонки и гонками данных. Большинство ответов, кажется, делает предположение, что эти условия эквивалентны, но они не.
Гонка данных происходит, когда 2 инструкции получают доступ к тому же местоположению памяти, по крайней мере один из этих доступов - писание и есть не , происходит прежде, чем заказать среди этих доступов. Теперь, что составляет происхождение, прежде чем заказ подвергнется большому количеству дебатов, но в общих парах ulock-замка на той же переменной замка и парах ждать-сигнала на том же условии переменная вызывает происхождение - перед заказом.
Условие гонки - семантическая ошибка. Это - недостаток, который происходит в выборе времени или заказе событий, который приводит к ошибочной программе behavior.
Много условий гонки могут быть (и на самом деле), вызванный гонками данных, но это не необходимо. На самом деле, гонки данных и условия гонки ни необходимое, ни достаточное условие друг для друга. [Это] (http://blog.regehr.org/archives/490) сообщение в блоге также объясняет различие очень хорошо с простым примером банковской сделки. Вот другое простое [пример] (https://stackoverflow.com/questions/11276259/are-data-races-and-race-condition-actually-the-same-thing-in-context-of-conc/18049303#18049303), который объясняет различие.
Теперь, когда мы прибили терминологию, давайте попытаемся ответить на оригинальный вопрос.
Учитывая, что условия гонки - семантические ошибки, нет никакого общего способа обнаружить их. Это вызвано тем, что нет никакого способа наличия автоматизированного оракула, который может различать правильный против неправильного поведения программы в общем случае. Обнаружение гонки - неразрешимая проблема.
С другой стороны, у гонок данных есть точное определение, которое не обязательно касается правильности, и поэтому можно обнаружить их. Есть много ароматов датчиков гонки данных (статическое/динамичное обнаружение гонки данных, основанное на системе замков обнаружение гонки данных, происходит - перед базирующимся обнаружением гонки данных, гибридным обнаружением гонки данных). Современный динамический датчик гонки данных [ThreadSanitizer] (https://code.google.com/p/data-race-test/wiki/ThreadSanitizer), который работает очень хорошо на практике.
Обработка гонок данных в целом требует, чтобы некоторая программная дисциплина, чтобы вызвать произошла - перед краями между доступами к совместно используемым данным (или во время развития, или как только они обнаружены, используя вышеупомянутые инструменты). это может быть сделано через замки, переменные условия, семафоры, и т.д. Однако можно также использовать различные программные парадигмы как прохождение сообщения (вместо совместно используемой памяти), которые избегают гонок данных строительством.
Своего рода определение - " when две нити получают доступ к тому же местоположению в памяти одновременно и по крайней мере одному из доступов, является write." В ситуации " reader" нить может получить старую стоимость или новую стоимость, в зависимости от который нить " выигрывает гонку " Это - не всегда bug& mdash; на самом деле некоторые действительно волосатые алгоритмы низкого уровня делают это на purpose& mdash; но этого нужно обычно избегать. @Steve Gury give' s хороший пример того, когда это могла бы быть проблема.
Состояние гонки - это вид ошибки, которая возникает только при определенных временных условиях.
Пример: Представьте, что у вас есть два потока, A и B.
В потоке A:
if( object.a != 0 )
object.avg = total / object.a
В потоке B:
object.a = 0
Если поток A будет вытеснен сразу после проверки того, что object.a не является null, B сделает a = 0
, а когда поток A получит процессор, он выполнит "деление на ноль".
Эта ошибка происходит только тогда, когда поток A вытесняется сразу после оператора if, это случается очень редко, но может произойти.
Условие гонки не только связано с программным обеспечением, но также и связано с аппаратными средствами также. На самом деле термин был первоначально введен промышленностью аппаратных средств.
Согласно Википедии:
термин начинается с идеи два сигнала, мчащиеся друг друга к влияйте на продукцию сначала .
условие Гонки в логической схеме:
Промышленность программного обеспечения взяла этот термин без модификации, которая делает немного трудным понять.
Вы должны сделать некоторую замену, чтобы нанести на карту его к миру программного обеспечения:
Таким образом, условие гонки в промышленности программного обеспечения означает " два threads"/" два processes" мчась друг друга к " влияйте некоторые разделили state" и конечный результат общего состояния будет зависеть от некоторого тонкого различия в выборе времени, которое могло быть вызвано некоторым определенным заказом запуска нити/процесса, планированием нити/процесса, и т.д.
Условия гонки происходят в многопоточных заявлениях или мультиобрабатывают системы. Условие гонки, в его самом основном, является чем-либо, что делает предположение, что две вещи не в той же нити или процессе произойдут в особом заказе, не предпринимая шаги, чтобы гарантировать, чтобы они сделали. Это происходит обычно, когда две нити передают сообщения, устанавливая, и проверка членских переменных класса оба может получить доступ. There' s почти всегда условие гонки, когда одна нить называет сон, чтобы дать другое время нити, чтобы закончить задачу (если тот сон не находится в петле с некоторым механизмом проверки).
Инструменты для предотвращения условий гонки зависят от языка и OS, но некоторые общие - mutexes, критические разделы и сигналы. Mutexes хороши, когда Вы хотите удостовериться you' ре единственное, делающее что-то. Сигналы хороши, когда Вы хотите удостовериться, что кто-то еще закончил делать что-то. Уменьшение общих ресурсов может также помочь предотвратить неожиданные поведения
Обнаружение условий гонки может быть трудным, но есть пара знаков. Кодекс, который полагается в большой степени на сны, подвержен условиям гонки, поэтому первой проверке на требования спать в затронутом кодексе. Добавление особенно длинных снов может также использоваться для отладки, чтобы попытаться вызвать особый заказ событий. Это может быть полезно для репродуцирования поведения, видя, можете ли Вы заставить его исчезнуть, изменив выбор времени вещей, и для тестирования решений, помещенных на месте. Сны должны быть удалены после отладки.
Знак подписи, что у каждого есть условие гонки, хотя, состоит в том если there' s проблема, которая только происходит периодически на некоторых машинах. Общие ошибки были бы катастрофами и тупиками. С регистрацией Вам необходимо найти зону поражения и работать назад оттуда.
Условие гонки - это ситуация в параллельном программировании, когда два параллельных потока или процесса конкурируют за ресурс, и конечное состояние зависит от того, кто получит ресурс первым.
Microsoft на самом деле издала действительно подробный статья об этом вопросе условий гонки и тупиков. Наиболее полученное в итоге резюме от него было бы параграфом названия:
условие гонки происходит, когда две нити получают доступ к общей переменной в то же время. Первая нить читает переменную и второе нить читает ту же стоимость от переменной. Тогда первая нить и вторая нить выполняет их действия на стоимости, и они мчатся видеть, какая нить может написать стоимость в последний раз общей переменной. ценность нити, которая пишет ее стоимость в последний раз, сохранена, потому что нить переписывает стоимость что предыдущее сообщение написал.
Что такое условие гонки?
Ситуация, когда процесс критически зависит от последовательности или выбора времени других событий.
Например, Процессор A и процессор B обе потребности идентичный ресурс для их выполнения.
Как Вы обнаруживаете их?
Есть инструменты, чтобы обнаружить условие гонки автоматически:
Как Вы обращаетесь с ними?
Условие гонки может быть обработано Mutex или Семафоры . Они действуют, поскольку замок позволяет процессу приобретать ресурс на основе определенных требований, чтобы предотвратить условие гонки.
Как Вы препятствуете тому, чтобы они произошли?
Есть различные способы предотвратить условие гонки, такой как Критическое Предотвращение Раздела .
[2]: http://citeseerx.ist.psu.edu/viewdoc/download? doi=10.1.1.39.9581& rep=rep1& type=pdf [3]: http://web.cs.msu.edu / ~ cse914/Readings/hybridDynamicRaceDetection-ppopp03.pdf
Вот классический пример Баланса Банковского счета, который поможет новичкам понять Нити на Яве легко w.r.t. условия гонки:
<! - язык: Ява - >
public class BankAccount {
/**
* @param args
*/
int accountNumber;
double accountBalance;
public synchronized boolean Deposit(double amount){
double newAccountBalance=0;
if(amount<=0){
return false;
}
else {
newAccountBalance = accountBalance+amount;
accountBalance=newAccountBalance;
return true;
}
}
public synchronized boolean Withdraw(double amount){
double newAccountBalance=0;
if(amount>accountBalance){
return false;
}
else{
newAccountBalance = accountBalance-amount;
accountBalance=newAccountBalance;
return true;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
BankAccount b = new BankAccount();
b.accountBalance=2000;
System.out.println(b.Withdraw(3000));
}
Условие гонки - нежелательная ситуация, которая происходит, когда устройство или система пытаются выполнить две или больше операции одновременно, но из-за природы устройства или системы, операции должны быть сделаны в надлежащей последовательности, чтобы быть сделанными правильно.
В памяти компьютера или хранении, может произойти условие гонки, если команды, чтобы читать и написать большой объем данных получены в почти тот же момент, и машина пытается переписать некоторых или все старые данные, в то время как те старые данные все еще читаются. Результат может быть одним или несколькими из следующего: компьютерная катастрофа, " незаконная операция, " уведомление и закрытие программы, ошибки, читая старые данные или ошибки, сочиняя новые данные.
Вы можете предотвращать условие гонки, если Вы используете " Atomic" классы. Причина - просто нить don' t отдельная операция получают и устанавливают, пример ниже:
AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);
В результате Вы будете иметь 7 в связи " ai". Хотя Вы сделали два действия, но и операция подтверждают ту же нить и никакую другую нить, вмешается к этому, которое не означает условий гонки!
Условие гонки - нежелательная ситуация, которая происходит, когда два или больше обрабатывают, может получить доступ и изменить совместно используемые данные одновременно. Это произошло, потому что там находились в противоречии доступы к ресурсу. Критическая проблема раздела может вызвать условие гонки. Чтобы решить критическое состояние среди процесса, мы имеем, вынимают только один процесс за один раз, которые выполняют критический раздел.
Попробуйте этот основной пример за лучшее понимание условия гонки:
public class ThreadRaceCondition {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Account myAccount = new Account(22222222);
// Expected deposit: 250
for (int i = 0; i < 50; i++) {
Transaction t = new Transaction(myAccount,
Transaction.TransactionType.DEPOSIT, 5.00);
t.start();
}
// Expected withdrawal: 50
for (int i = 0; i < 50; i++) {
Transaction t = new Transaction(myAccount,
Transaction.TransactionType.WITHDRAW, 1.00);
t.start();
}
// Temporary sleep to ensure all threads are completed. Don't use in
// realworld :-)
Thread.sleep(1000);
// Expected account balance is 200
System.out.println("Final Account Balance: "
+ myAccount.getAccountBalance());
}
}
class Transaction extends Thread {
public static enum TransactionType {
DEPOSIT(1), WITHDRAW(2);
private int value;
private TransactionType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
};
private TransactionType transactionType;
private Account account;
private double amount;
/*
* If transactionType == 1, deposit else if transactionType == 2 withdraw
*/
public Transaction(Account account, TransactionType transactionType,
double amount) {
this.transactionType = transactionType;
this.account = account;
this.amount = amount;
}
public void run() {
switch (this.transactionType) {
case DEPOSIT:
deposit();
printBalance();
break;
case WITHDRAW:
withdraw();
printBalance();
break;
default:
System.out.println("NOT A VALID TRANSACTION");
}
;
}
public void deposit() {
this.account.deposit(this.amount);
}
public void withdraw() {
this.account.withdraw(amount);
}
public void printBalance() {
System.out.println(Thread.currentThread().getName()
+ " : TransactionType: " + this.transactionType + ", Amount: "
+ this.amount);
System.out.println("Account Balance: "
+ this.account.getAccountBalance());
}
}
class Account {
private int accountNumber;
private double accountBalance;
public int getAccountNumber() {
return accountNumber;
}
public double getAccountBalance() {
return accountBalance;
}
public Account(int accountNumber) {
this.accountNumber = accountNumber;
}
// If this method is not synchronized, you will see race condition on
// Remove syncronized keyword to see race condition
public synchronized boolean deposit(double amount) {
if (amount < 0) {
return false;
} else {
accountBalance = accountBalance + amount;
return true;
}
}
// If this method is not synchronized, you will see race condition on
// Remove syncronized keyword to see race condition
public synchronized boolean withdraw(double amount) {
if (amount > accountBalance) {
return false;
} else {
accountBalance = accountBalance - amount;
return true;
}
}
}
Вы don' t всегда хочу отказаться от условия гонки. Если у Вас есть флаг, который может быть прочитан и написан несколькими нитями, и этот флаг установлен в ' done' одной нитью так, чтобы другая обработка остановки нити, когда флаг установлен в ' done' Вы don' t хотят это " гонка condition" быть устраненным. На самом деле этот может упоминаться как мягкое условие гонки.
Однако используя инструмент для обнаружения условия гонки, это будет определено как вредное условие гонки.
Больше деталей об условии гонки здесь, http://msdn.microsoft.com/en-us/magazine/cc546569.aspx.
Рассмотрите операцию, которая должна показать количество, как только количество увеличено. т.е., как только CounterThread увеличивает стоимость , DisplayThread должен показать недавно обновленную стоимость.
int i = 0;
Продукция
CounterThread -> i = 1
DisplayThread -> i = 1
CounterThread -> i = 2
CounterThread -> i = 3
CounterThread -> i = 4
DisplayThread -> i = 4
Здесь CounterThread часто получает замок и обновляет стоимость прежде , DisplayThread показывает его. Здесь существует условие Гонки. Условие гонки может быть решено при помощи Synchronzation