Simple Guide to Python Threading

Jul 29, 2019

Why use Threading

  • Use threading for IO bound task, or consider Asyncio.
  • Not suitable for CPU bound task due to GIL and utilizing single core only. Consider Multiprocessing for CPU bound task.
  • Can use shared variable for communication between threads, probably require a lock or thread-safe queue.
  • Very hard to write and maintain correctness of code: think again before attempting a complex solution with threading.

NOTE: Refer Python Threading vs Multiprocessing vs Asyncio.

Launch a thread.

import threadingdef run():    print('run')threading.Thread(target=run).start()

Pass named argument into thread

def run(name):    print(f"run: {name}")threading.Thread(target=run).start(kwargs={'name': 'test'})

Deamon thread (infinite loop)

import threadingimport timeis_running = Truedef run():    while is_running:        print('.', end='', flush=True)        time.sleep(1)t = threading.Thread(target=run)t.daemon = Truet.start()del t# stop daemon thread after 10stime.sleep(10)is_running = False

Shared variable

Atomic operation should be thread-safe

import timeimport randomimport threadingcurrent_time = 0is_loop = Truedef run():    global current_time    time.sleep(random.random() * 0.1)    current_time = time.time()def print_time():    last_time = 0    count = 0    while is_loop:        if current_time != last_time:            count += 1            print(f"{count}={current_time}")            last_time = current_time    print('print_time end: ')for i in range(10):    threading.Thread(target=run).start()t = threading.Thread(target=print_time)t.daemon = Truet.start()del ttime.sleep(2)print(f'stop print_time')is_loop = False

As you have notice above, print_time access to current_time is not truely atomic. We are doing checking (current_time != last_time) followed by printing (print(f"{count}={current_time}")) where the value could have changed in between these operation.

A truly atomic print_time access to current_time would be:

def print_time():    while True:        print(f"{current_time}")

NOTE: It is still possible to miss out printing one or two changes to current_time, as current_time could changed twice before print_time is executed.

Lock

  • Acquire a lock just before assign the value (prevent other thread to assign a new value)
  • Release the lock after printing the value (allow other thread to assign a new value)
import timeimport randomimport threadingcurrent_time = 0current_time_lock = threading.Lock()is_loop = Truedef run():    global current_time    time.sleep(random.random() * 0.1)    current_time_lock.acquire()    current_time = time.time()def print_time():    last_time = 0    count = 0    while is_loop:        if current_time != last_time:            count += 1            print(f"{count}={current_time}")            last_time = current_time            current_time_lock.release()    print('print_time end: ')for i in range(10):    threading.Thread(target=run).start()t = threading.Thread(target=print_time)t.daemon = Truet.start()del ttime.sleep(2)print(f'stop print_time')is_loop = False

Queue

Python queue is thread-safe.

import timeimport randomimport threadingimport queuecurrent_time_queue = queue.Queue()is_loop = Truedef run():    time.sleep(random.random() * 0.1)    current_time = time.time()    current_time_queue.put(current_time)def print_time():    count = 0    while is_loop:        current_time = current_time_queue.get()        if current_time != None:            count += 1            print(f"{count}={current_time}")    print('print_time end: ')for i in range(10):    threading.Thread(target=run).start()t = threading.Thread(target=print_time)t.daemon = Truet.start()del ttime.sleep(2)print(f'stop print_time')is_loop = False

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.