Race Conditions
In this lesson, you’ll learn that it’s very important to consider race conditions when you’re working with multi-threaded applications. A race condition happens when more than one thread is trying to access a shared piece of data at the same time.
00:00 In this lesson, let’s talk about something very important to consider when we’re working with multi-threaded applications—and these are race conditions.
00:10 Let’s say we have some shared data that two threads want to use at the same time. We have an account balance of $100, and we have a withdrawal thread on the left and a deposit thread on the right.
00:24 What happens when both of these threads try to access this shared data, the account balance, at the same time? We don’t really have a solid understanding of what the balance is.
00:36 It depends on how much we’re depositing and how much we’re trying to withdraw.
00:42 Let’s say the account balance was at $100 and somehow the withdrawal thread made it to that shared data before the deposit did. And this is a very possible and often common occurrence with multi-threading, as we’ve seen before, is that sometimes threads don’t finish and get executed in the order that we would expect.
01:04 This withdrawal thread made it to the balance before the deposit did, and this is going to result in some unexpected, confusing behavior that the user will have to experience. So this is really bad.
01:18 What we want to happen is the deposit thread to reach the balance first, so we can deposit $50, and then that thread can go and finish its execution. Then the withdrawal thread can now successfully withdraw $150, and then the balance gets updated to its correct amount.
01:39 So, if we go back here, we see the essence of what a race condition is. It’s when more than one thread is trying to access a shared piece of data at the same time.
Bartosz Zaczyński RP Team on June 1, 2022
@hwoarang09 A race condition means that your program’s result might depend on the order of execution of the individual steps. This becomes essential in concurrent programs that can run multiple threads of execution simultaneously through context switching, and they access a shared resource like a variable.
The classic example of a race condition is when making a bank transfer from one account to another without using locks to make this operation transactional or atomic, i.e., running as a whole without interruptions. This could result in leaving the state of both accounts inconsistent. For instance, the target account would receive the money, but the source one would still have the initial amount or the other way around. You’d send the money away without getting it on the other end.
CPython’s GIL always puts implicit locks or guards around the same number of bytecode instructions, more or less, but can’t prevent the scheduler from interrupting a longer block of code that should run in a transaction. To secure such a critical section in your code, you must use explicit locks or other techniques yourself. Python has a few high-level abstractions for coordinating and synchronizing threads in the threading module, such as locks, conditions, semaphores, events, timers, and barriers.
hwoarang09 on June 7, 2022
Thanks for the answer, but I can’t understand what GIL does exactly. Maybe it is hard to understand what bytecode is. (I googled some articles, blogs about bytecode, but it is very difficult to understand.)
If I excute a code like local_copy += amount
, I think, GIL didn’t protect that one line code. If GIL protected one line Python code, then race condition can’t occur.
In my opinion, in one line code, maybe there are three steps, read local_copy, increment, write to local_copy, Right? Then, thread1 reads local_copy, GIL prevents thread2 from reading local_copy, after thread1 reads copy, GIL unlocks and thread2 reads local_copy. Is it right?
It is very hard to understand, to google. Please help me.
Bartosz Zaczyński RP Team on June 7, 2022
@hwoarang09 You don’t need to understand GIL to write good code. GIL is an implementation detail of the CPython interpreter, which primarily prevents multithreaded code from running in parallel. Other than that, you shouldn’t care about GIL. Instead, always put explicit locking or use thread-safe data structures in multithreaded code.
hwoarang09 on June 8, 2022
Okay, i don’t care GIL. it makes me crazy. Thanks!! other people also tell me just use critical section, lock. Thanks!
Become a Member to join the conversation.
hwoarang09 on May 31, 2022
I heard Python uses GIL to prevent race conditions, but in the lecture, a race condition happend. I want to know what it is… Thanks!