What is a race condition?

后端 未结 18 2497
谎友^
谎友^ 2020-11-21 04:52

When writing multithreaded applications, one of the most common problems experienced is race conditions.

My questions to the community are:

What is the rac

相关标签:
18条回答
  • 2020-11-21 05:02

    There is an important technical difference between race conditions and data races. Most answers seem to make the assumption that these terms are equivalent, but they are not.

    A data race occurs when 2 instructions access the same memory location, at least one of these accesses is a write and there is no happens before ordering among these accesses. Now what constitutes a happens before ordering is subject to a lot of debate, but in general ulock-lock pairs on the same lock variable and wait-signal pairs on the same condition variable induce a happens-before order.

    A race condition is a semantic error. It is a flaw that occurs in the timing or the ordering of events that leads to erroneous program behavior.

    Many race conditions can be (and in fact are) caused by data races, but this is not necessary. As a matter of fact, data races and race conditions are neither the necessary, nor the sufficient condition for one another. This blog post also explains the difference very well, with a simple bank transaction example. Here is another simple example that explains the difference.

    Now that we nailed down the terminology, let us try to answer the original question.

    Given that race conditions are semantic bugs, there is no general way of detecting them. This is because there is no way of having an automated oracle that can distinguish correct vs. incorrect program behavior in the general case. Race detection is an undecidable problem.

    On the other hand, data races have a precise definition that does not necessarily relate to correctness, and therefore one can detect them. There are many flavors of data race detectors (static/dynamic data race detection, lockset-based data race detection, happens-before based data race detection, hybrid data race detection). A state of the art dynamic data race detector is ThreadSanitizer which works very well in practice.

    Handling data races in general requires some programming discipline to induce happens-before edges between accesses to shared data (either during development, or once they are detected using the above mentioned tools). this can be done through locks, condition variables, semaphores, etc. However, one can also employ different programming paradigms like message passing (instead of shared memory) that avoid data races by construction.

    0 讨论(0)
  • 2020-11-21 05:03

    A "race condition" exists when multithreaded (or otherwise parallel) code that would access a shared resource could do so in such a way as to cause unexpected results.

    Take this example:

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

    If you had 5 threads executing this code at once, the value of x WOULD NOT end up being 50,000,000. It would in fact vary with each run.

    This is because, in order for each thread to increment the value of x, they have to do the following: (simplified, obviously)

    Retrieve the value of x
    Add 1 to this value
    Store this value to x
    

    Any thread can be at any step in this process at any time, and they can step on each other when a shared resource is involved. The state of x can be changed by another thread during the time between x is being read and when it is written back.

    Let's say a thread retrieves the value of x, but hasn't stored it yet. Another thread can also retrieve the same value of x (because no thread has changed it yet) and then they would both be storing the same value (x+1) back in x!

    Example:

    Thread 1: reads x, value is 7
    Thread 1: add 1 to x, value is now 8
    Thread 2: reads x, value is 7
    Thread 1: stores 8 in x
    Thread 2: adds 1 to x, value is now 8
    Thread 2: stores 8 in x
    

    Race conditions can be avoided by employing some sort of locking mechanism before the code that accesses the shared resource:

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

    Here, the answer comes out as 50,000,000 every time.

    For more on locking, search for: mutex, semaphore, critical section, shared resource.

    0 讨论(0)
  • 2020-11-21 05:03

    What is a race condition?

    The situation when the process is critically dependent on the sequence or timing of other events.

    For example, Processor A and processor B both needs identical resource for their execution.

    How do you detect them?

    There are tools to detect race condition automatically:

    • Lockset-Based Race Checker
    • Happens-Before Race Detection
    • Hybrid Race Detection

    How do you handle them?

    Race condition can be handled by Mutex or Semaphores. They act as a lock allows a process to acquire a resource based on certain requirements to prevent race condition.

    How do you prevent them from occurring?

    There are various ways to prevent race condition, such as Critical Section Avoidance.

    1. No two processes simultaneously inside their critical regions. (Mutual Exclusion)
    2. No assumptions are made about speeds or the number of CPUs.
    3. No process running outside its critical region which blocks other processes.
    4. No process has to wait forever to enter its critical region. (A waits for B resources, B waits for C resources, C waits for A resources)
    0 讨论(0)
  • 2020-11-21 05:07

    Microsoft actually have published a really detailed article on this matter of race conditions and deadlocks. The most summarized abstract from it would be the title paragraph:

    A race condition occurs when two threads access a shared variable at the same time. The first thread reads the variable, and the second thread reads the same value from the variable. Then the first thread and second thread perform their operations on the value, and they race to see which thread can write the value last to the shared variable. The value of the thread that writes its value last is preserved, because the thread is writing over the value that the previous thread wrote.

    0 讨论(0)
  • 2020-11-21 05:07

    Try this basic example for better understanding of race condition:

        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;
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 05:12

    A race condition is an undesirable situation that occurs when a device or system attempts to perform two or more operations at the same time, but because of the nature of the device or system, the operations must be done in the proper sequence in order to be done correctly.

    In computer memory or storage, a race condition may occur if commands to read and write a large amount of data are received at almost the same instant, and the machine attempts to overwrite some or all of the old data while that old data is still being read. The result may be one or more of the following: a computer crash, an "illegal operation," notification and shutdown of the program, errors reading the old data, or errors writing the new data.

    0 讨论(0)
提交回复
热议问题