Multithreading (Python)

What’s a Thread?

  • Mechanism for parallel code execution

  • ⟶ more than one flow of control in the same address space

  • Issues

    • Startup/shutdown

    • Sharing data: concurrent access, and protecting against

    • Communication between threads

Threads: The Pythonic Way

import threading
import time

def increment_background():
    global background_integer
    while True:
        print('background: value is', background_integer)
        time.sleep(0.5)
        background_integer += 1

background_integer = 0
t = threading.Thread(target=increment_background)
t.start()

foreground_integer = 0
while True:
    print('foreground: value is', foreground_integer)
    time.sleep(0.5)
    foreground_integer -= 1
$ python code/thread-pythonic.py
background: value is 0
foreground: value is 0
background: value is 1
foreground: value is -1
background: value is 2
foreground: value is -2
background: value is 3
foreground: value is -3
background: value is 4
...

Two threads exist, runnning in parallel

  • Main thread (not explicitly created)

  • Background integer incrementer (explicitly created)

Threads: The OO Way - Inheritance

  • Inherit from threading.Thread

  • Inheritance is a mechanism for late binding, brought to the world by languages that have no other mechanism

  • In Python there is duck typing

  • This method of starting a thread is equivalent to the pythonic method, but should not be used (trainer’s opinion only, though)

Anyway:

import threading
import time

class BackgroundIncrementer(threading.Thread):
    def run(self):
        global background_integer
        while True:
            print('background: value is', background_integer)
            time.sleep(0.5)
            background_integer += 1

background_integer = 0
t = BackgroundIncrementer()
t.start()

foreground_integer = 0
while True:
    print('foreground: value is', foreground_integer)
    time.sleep(0.5)
    foreground_integer -= 1
  • Uncool: inheritance is not strictly Pythonic (at least not for such kinds of relationship)

  • Uncool: people might not even know what inheritance is

And Program Termination?

Problem: threads continue running when main thread terminates (spoiler: daemon threads don’t)

import threading
import time

def increment_background():
    global background_integer
    while True:
        print('background: value is', background_integer)
        time.sleep(0.5)
        background_integer += 1

background_integer = 0
t = threading.Thread(target=increment_background)
t.start()

foreground_integer = 0
for _ in range(3):
    print('foreground: value is', foreground_integer)
    time.sleep(0.5)
    foreground_integer -= 1
$ python code/thread-program-termination.py
background: value is 0
foreground: value is 0
background: value is 1
foreground: value is -1
background: value is 2
foreground: value is -2  # <--- last notice of main thread
background: value is 3
background: value is 4
background: value is 5
background: value is 6
background: value is 7
background: value is 8
...   # only background thread running

Daemon Threads

Definition

  • A program exits when only daemon threads are left

  • The main thread is not a daemon thread

Attention

This definition is Python specific!

Corollary: if I only create daemon threads, then program exit is

fine.

import threading
import time

def increment_background():
    global background_integer
    while True:
        print('background: value is', background_integer)
        time.sleep(0.5)
        background_integer += 1

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

foreground_integer = 0
for _ in range(3):
    print('foreground: value is', foreground_integer)
    time.sleep(0.5)
    foreground_integer -= 1

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_0140_variables Variables python_basics_python_0130_syntax_etc Syntax etc. python_basics_python_0140_variables->python_basics_python_0130_syntax_etc python_basics_python_0150_datatypes_overview Datatypes python_basics_python_0150_datatypes_overview->python_basics_python_0140_variables python_basics_python_0110_blahblah Blahblah python_basics_python_0120_helloworld Hello World python_basics_python_0120_helloworld->python_basics_python_0110_blahblah python_basics_python_0130_syntax_etc->python_basics_python_0120_helloworld python_basics_python_0150_datatypes_overview_compound Compound Datatypes python_basics_python_0150_datatypes_overview_compound->python_basics_python_0150_datatypes_overview python_advanced_multithreading_basics Multithreading (Python) python_advanced_oo_inheritance Inheritance python_advanced_multithreading_basics->python_advanced_oo_inheritance python_advanced_oo_constructor Constructor python_advanced_oo_classes_and_dicts Classes And Dictionaries python_advanced_oo_constructor->python_advanced_oo_classes_and_dicts python_advanced_oo_classes_and_dicts->python_basics_python_0150_datatypes_overview_compound python_advanced_oo_inheritance->python_advanced_oo_constructor python_advanced_oo_inheritance->python_advanced_oo_classes_and_dicts