Что такое состояние гонки?

multithreading concurrency terminology race-condition

456861 просмотра

18 ответа

9045 Репутация автора

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

Мои вопросы для сообщества:

Что такое состояние гонки? Как вы их обнаруживаете? Как вы справляетесь с ними? Наконец, как вы их предотвращаете?

Автор: bmurphy1976 Источник Размещён: 29.08.2008 03:55

Ответы (18)


29 плюса

10216 Репутация автора

Состояние гонки - это своего рода ошибка, которая происходит только с определенными временными условиями.

Пример. Представьте, что у вас есть два потока: A и B.

В потоке A:

if( object.a != 0 )
    object.avg = total / object.a

В потоке B:

object.a = 0

Если поток A выгружен сразу после проверки того, что object.a не является нулевым, B будет делать a = 0, и когда поток A получит процессор, он сделает «деление на ноль».

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

Автор: Steve Gury Размещён: 29.08.2008 04:03

968 плюса

21980 Репутация автора

Решение

Условие гонки возникает, когда два или более потока могут обращаться к общим данным, и они пытаются изменить его в одно и то же время. Поскольку алгоритм планирования потоков может меняться между потоками в любое время, вы не знаете порядок, в котором потоки будут пытаться получить доступ к общим данным. Следовательно, результат изменения данных зависит от алгоритма планирования потоков, то есть оба потока являются «гоночными» для доступа / изменения данных.

Часто возникают проблемы, когда один поток выполняет «check-then-act» (например, «check», если значение 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
Автор: Lehane Размещён: 29.08.2008 04:05

5 плюса

31281 Репутация автора

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

Автор: Jorge Córdoba Размещён: 29.08.2008 04:07

18 плюса

1452 Репутация автора

Условия гонки происходят в многопоточных приложениях или многопроцессорных системах. Условие гонки, в его самом базовом, - это все, что делает предположение, что две вещи не в одном потоке или процессе не будут выполняться в определенном порядке, не предпринимая шагов для обеспечения их выполнения. Обычно это происходит, когда два потока передают сообщения, устанавливая и проверяя переменные-члены класса, к которым могут обращаться. Почти всегда существует условие гонки, когда один поток вызывает сон, чтобы дать другое время для завершения задачи (если этот сон не находится в цикле, с некоторым механизмом проверки).

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

Обнаружение условий гонки может быть затруднительным, но есть пара знаков. Код, который в значительной степени зависит от сна, склонен к условиям гонки, поэтому сначала проверьте, чтобы вызовы спать в затронутом коде. Добавление особенно длинных снов также может быть использовано для отладки, чтобы попытаться заставить определенный порядок событий. Это может быть полезно для воспроизведения поведения, если вы можете заставить его исчезнуть, изменив время и вещи для тестирования. Сны должны быть удалены после отладки.

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

Автор: tsellon Размещён: 29.08.2008 04:12

33 плюса

39062 Репутация автора

Сорто-каноническое определение - « когда два потока одновременно обращаются в одно и то же место в памяти, и по крайней мере один из обращений является записью ». В ситуации поток читателя может получить старое значение или новое значение, в зависимости от того, какой поток «выигрывает гонку». Это не всегда ошибка - на самом деле некоторые действительно волосатые алгоритмы низкого уровня делают это специально, но этого обычно следует избегать. @Steve Gury дать хороший пример того, когда это может быть проблемой.

Автор: Chris Conway Размещён: 29.08.2008 04:21

182 плюса

2064 Репутация автора

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

Возьмем следующий пример:

for ( int i = 0; i < 10000000; i++ )
{
   x = x + 1; 
}

Если у вас было 5 потоков, выполняющих этот код сразу, значение x не должно составлять 50 000 000. На самом деле это будет зависеть от каждого прогона.

Это связано с тем, что для того, чтобы каждый поток увеличивал значение x, они должны делать следующее: (упрощенно, очевидно)

Получить значение x
Добавьте 1 к этому значению
Сохраните это значение до x

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

Предположим, что поток получает значение x, но еще не сохранил его. Другой поток также может получить одно и то же значение x (потому что ни один нить не изменил его), а затем они оба сохраняют одно и то же значение (x + 1) обратно в x!

Пример:

Тема 1: читает x, значение равно 7
Тема 1: добавить 1 в x, теперь значение 8
Thread 2: читает x, значение равно 7
Тема 1: хранит 8 в x
Тема 2: добавляет 1 к x, теперь значение 8
Тема 2: сохраняет 8 в x

Условия гонки можно избежать, используя механизм блокировки перед кодом, который обращается к общему ресурсу:

for ( int i = 0; i < 10000000; i++ )
{
   //lock x
   x = x + 1; 
   //unlock x
}

Здесь каждый ответ приходит на 50 000 000.

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

Автор: privatehuff Размещён: 29.08.2008 05:01

2 плюса

658 Репутация автора

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

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));

}
Автор: realPK Размещён: 22.11.2011 05:35

2 плюса

49 Репутация автора

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

В памяти компьютера или в хранилище может возникнуть условие гонки, если команды для чтения и записи большого количества данных принимаются почти в одно и то же мгновение, и машина пытается перезаписать некоторые или все старые данные, пока старые данные все еще находятся читать. Результатом может быть одно или несколько из следующего: компьютерная авария, «незаконная операция», уведомление и завершение работы программы, ошибки чтения старых данных или ошибки записи новых данных.

Автор: dilbag koundal Размещён: 13.04.2012 11:29

7 плюса

24401 Репутация автора

Microsoft действительно опубликовала действительно подробную статью по этому вопросу о состоянии гонки и тупиках. Наиболее суммарным из них будет абзац заголовка:

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

Автор: Konstantin Dinev Размещён: 14.09.2012 08:00

0 плюса

1 Репутация автора

Попробуйте этот базовый пример для лучшего понимания состояния гонки:

    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;
        }
    }
}
Автор: Morsu Размещён: 31.05.2013 03:51

53 плюса

1531 Репутация автора

Существует важная техническая разница между условиями гонки и гонками данных. Большинство ответов, похоже, делают предположение, что эти условия эквивалентны, но они не являются.

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

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

Многие условия гонки могут (и на самом деле) вызваны гонками данных, но это необязательно. На самом деле, расы данных и условия гонки не являются ни необходимым, ни достаточным условием для друг друга. Это сообщение в блоге также объясняет разницу очень хорошо, с простым примером банковских транзакций. Вот еще один простой пример , объясняющий разницу.

Теперь, когда мы прикрепили терминологию, попробуем ответить на исходный вопрос.

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

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

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

Автор: Baris Kasikci Размещён: 29.08.2013 08:45

118 плюса

1592 Репутация автора

Что такое состояние гонки?

Вы планируете пойти в кино в 17:00. Вы спрашиваете о наличии билетов в 16:00. Представитель говорит, что они доступны. Вы расслабляетесь и добираетесь до окна билета за 5 минут до шоу. Я уверен, что вы можете догадаться, что происходит: это фулл-хаус. Проблема здесь заключалась в продолжительности между проверкой и действием. Вы спросили 4 и выступили в 5. В то же время кто-то еще схватил билеты. Это состояние гонки - это, в частности, сценарий гоночных условий «check-then-act».

Как вы их обнаруживаете?

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

Как вы справляетесь и предотвращаете их?

Лучше всего было бы создать свободные побочные эффекты и функции без гражданства, максимально использовать неизменяемые. Но это не всегда возможно. Таким образом, использование java.util.concurrent.atomic, параллельные структуры данных, правильная синхронизация и совместное использование на основе актера помогут.

Лучшим ресурсом для параллелизма является JCIP. Здесь вы также можете получить более подробную информацию об этом объяснении .

Автор: Vishal Shukla Размещён: 04.10.2013 09:20

0 плюса

13625 Репутация автора

Вы не всегда хотите отказаться от состояния гонки. Если у вас есть флаг, который может быть прочитан и написан несколькими потоками, и этот флаг установлен на «done» одним потоком, так что другая обработка прекращения потока, когда флаг установлен на «done», вы не хотите, чтобы «гонка» условие "будет устранено. Фактически, это можно назвать доброкачественным состоянием гонок.

Однако, используя инструмент для определения состояния гонки, он будет замечен как опасное состояние гонки.

Подробнее о состоянии гонки здесь, http://msdn.microsoft.com/en-us/magazine/cc546569.aspx .

Автор: octoback Размещён: 07.09.2014 09:11

2 плюса

42 Репутация автора

Хорошо, это 4 вопроса. один за другим ответ есть как под ....

Что такое состояние гонки?

Это происходит, когда выход и / или результат процесса критически зависят от последовательности или времени других событий. Итог 2 сигналов гоняется, чтобы сначала изменить выход.

Как вы их обнаруживаете?

Это приводит к ошибке, которую трудно локализовать.

Как вы справляетесь с ними?

Использовать семафоры

И наконец,

Как вы их предотвращаете?

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

Автор: Adnan Qureshi Размещён: 14.11.2014 01:43

0 плюса

1318 Репутация автора

Рассмотрим операцию, которая должна отображать счет, как только счет будет увеличен. т.е., как только 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

Автор: bharanitharan Размещён: 15.07.2015 08:00

12 плюса

3792 Репутация автора

Состояние гонки связано не только с программным обеспечением, но и с аппаратным обеспечением. На самом деле термин первоначально был придуман аппаратной отраслью.

Согласно википедии :

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

Состояние гонки в логической схеме:

введите описание изображения здесь

Программная индустрия приняла этот термин без изменений, что делает его немного трудным для понимания.

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

  • «два сигнала» => «два потока» / «два процесса»
  • «влиять на выход» => «влиять на какое-то общее состояние»

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

Автор: nybon Размещён: 04.08.2017 03:57

0 плюса

31 Репутация автора

Вы можете предотвратить состояние гонки , если используете классы «Atomic». Причина в том, что поток не разделяет операции get и set, пример ниже:

AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);

В результате у вас будет 7 в ссылке «ai». Хотя вы сделали два действия, но обе операции подтверждают тот же поток, и ни один другой поток не будет вмешиваться в это, это означает отсутствие условий гонки!

Автор: Aleksei Moshkov Размещён: 12.08.2017 02:48

0 плюса

1024 Репутация автора

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

Автор: rashedcs Размещён: 05.12.2018 06:55
32x32