Race Conditions, and Mutexes

Mother of All Race conditions: Integer Increment

import threading

integer = 0
ntimes = 10000000

def increment_background():
    global integer, ntimes
    for _ in range(ntimes):
        integer += 1

t = threading.Thread(target=increment_background,
                     daemon=True)    # <----- mark as daemon
t.start()

for _ in range(ntimes):
    integer += 1

print('final value:', integer)
$ python code/race-condition.py
final value: 14784035

Load Modify Store Conflict

Load Modify Store Conflict

CPU A

CPU B

Memory

Instr

Loc

Instr

Loc

Glob

load

42

42

42

load

42

42

inc

43

42

43

inc

43

42

43

store

43

43

store

43

43

43

Solution: Mutex (Explicit Acquire/Release)

  • Mutex (for “MUTual EXclusion”), short: Lock

  • Best analogy: toilet door lock

import threading

integer = 0
ntimes = 10000000
lock = threading.Lock()

def increment_background():
    global integer, ntimes
    for _ in range(ntimes):
        lock.acquire()
        integer += 1
        lock.release()

t = threading.Thread(target=increment_background,
                     daemon=True)    # <----- mark as daemon
t.start()

for _ in range(ntimes):
    lock.acquire()
    integer += 1
    lock.release()

print('final value:', integer)
$ python code/mutex.py
final value: 20000000

Solution: Mutex (with Statement)

  • Calling lock.acquire() and lock.release() manually

  • ⟶ what if exception is raised in the middle? (Well, integer increments don’t usually raise one; you get the point though.)

⟶ This is what the with statement is there fore (threading.Lock can act as a context manager)

import threading

integer = 0
ntimes = 10000000
lock = threading.Lock()

def increment_background():
    global integer, ntimes
    for _ in range(ntimes):
        with lock:
            integer += 1


t = threading.Thread(target=increment_background,
                     daemon=True)    # <----- mark as daemon
t.start()

for _ in range(ntimes):
    with lock:
        integer += 1

print('final value:', integer)

Dependencies

cluster_python Python Programming: From Absolute Beginner to Advanced Productivity cluster_python_basics Python: The Language Fundamentals cluster_python_advanced Python: More Language Features cluster_python_advanced_multithreading Multithreading cluster_python_advanced_oo Object Oriented Programming python_basics_python_0150_datatypes_overview_compound Compound Datatypes python_basics_python_0150_datatypes_overview Datatypes python_basics_python_0150_datatypes_overview_compound->python_basics_python_0150_datatypes_overview python_basics_python_0120_helloworld Hello World python_basics_python_0110_blahblah Blahblah python_basics_python_0120_helloworld->python_basics_python_0110_blahblah python_basics_python_0130_syntax_etc Syntax etc. python_basics_python_0130_syntax_etc->python_basics_python_0120_helloworld python_basics_python_0140_variables Variables python_basics_python_0150_datatypes_overview->python_basics_python_0140_variables python_basics_python_0140_variables->python_basics_python_0130_syntax_etc python_advanced_multithreading_mutex Race Conditions, and Mutexes python_advanced_multithreading_basics Multithreading (Python) python_advanced_multithreading_mutex->python_advanced_multithreading_basics python_advanced_oo_inheritance Inheritance python_advanced_multithreading_basics->python_advanced_oo_inheritance python_advanced_oo_classes_and_dicts Classes And Dictionaries python_advanced_oo_classes_and_dicts->python_basics_python_0150_datatypes_overview_compound python_advanced_oo_constructor Constructor python_advanced_oo_constructor->python_advanced_oo_classes_and_dicts python_advanced_oo_inheritance->python_advanced_oo_classes_and_dicts python_advanced_oo_inheritance->python_advanced_oo_constructor