Что такое состояние гонки? - PullRequest
851 голосов
/ 29 августа 2008

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

Мои вопросы к сообществу:

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

Ответы [ 19 ]

3 голосов
/ 14 ноября 2014

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

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

Например, Процессору A и процессору B оба необходимы идентичные ресурсы для их выполнения.

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

Существуют инструменты для автоматического определения состояния гонки:

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

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

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

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

  1. Нет двух процессов одновременно внутри их критических областей. ( Взаимное исключение)
  2. Не делается никаких предположений о скорости или количестве процессоров.
  3. Нет процессов, запущенных вне критической области, которая блокирует другие процессы.
  4. Никакому процессу не нужно ждать вечно, чтобы войти в критическую область. (A ждет ресурсов B, B ждет ресурсов C, C ждет ресурсов A)
2 голосов
/ 22 ноября 2011

Вот классический пример сальдо банковского счета, который поможет новичкам легко понять нити в Java 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));

}
2 голосов
/ 13 апреля 2012

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

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

0 голосов
/ 12 августа 2017

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

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

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

0 голосов
/ 15 июля 2015

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

0 голосов
/ 07 сентября 2014

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

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

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

0 голосов
/ 05 декабря 2018

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

0 голосов
/ 13 апреля 2019
public class Synchronized_RACECONDITION {
    private static final int NUM_INCREMENTS = 10000;

    private static int count = 0;

    public static void main(String[] args) {
        testSyncIncrement();
        testNonSyncIncrement();
    }

    private static void testSyncIncrement() {
        count = 0;

        ExecutorService executor = Executors.newFixedThreadPool(2);

        IntStream.range(0, NUM_INCREMENTS)
                .forEach(i -> executor.submit(Synchronized_RACECONDITION::incrementSync));

        ConcurrentUtils.stop(executor);

        System.out.println("   Sync: " + count);
    }

    private static void testNonSyncIncrement() {
        count = 0;

        ExecutorService executor = Executors.newFixedThreadPool(2);

        IntStream.range(0, NUM_INCREMENTS)
                .forEach(i -> executor.submit(Synchronized_RACECONDITION::increment));

        ConcurrentUtils.stop(executor);

        System.out.println("NonSync: " + count);
    }

    private static synchronized void incrementSync() {
        count = count + 1;
    }

    private static void increment() {
        count = count + 1;
    }
static  class ConcurrentUtils {

    public static void stop(ExecutorService executor) {
        try {
            executor.shutdown();
            executor.awaitTermination(60, TimeUnit.SECONDS);
        }
        catch (InterruptedException e) {
            System.err.println("termination interrupted");
        }
        finally {
            if (!executor.isTerminated()) {
                System.err.println("killing non-finished tasks");
            }
            executor.shutdownNow();
        }
    }
}
}
0 голосов
/ 31 мая 2013

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

    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;
        }
    }
}
...