Python Threading: Concepts, Creation Methods, Daemon Threads, and Synchronization with Locks
This article explains the fundamentals of Python threading, covering thread definitions, the threading module, two ways to create threads, daemon thread behavior, common thread‑safety issues, and how to use mutex locks to synchronize access to shared resources.
Threads, also called lightweight processes, are the smallest unit of execution flow in a program, consisting of a thread ID, program counter, registers, and a stack, and share the resources of their parent process.
Python provides two standard library modules for threading: _thread (low‑level) and threading (high‑level). The article focuses on the threading module, which is used for most real‑world development.
Creating a Thread object follows the syntax threading.Thread(target=None, name=None, args=()) . Important parameters are target (the function to run), name (thread name), and args (function arguments as a tuple). Common methods include start() , run() , join() , isAlive() , getName() , and setName() .
<code>import threading
threading.Thread(target=None, name=None, args=())</code>Two ways to start threads are demonstrated:
1) Function‑based creation – pass a function and its arguments to Thread , start the threads, and optionally join them.
<code>import threading, time, random, math
def printNum(idx):
for num in range(idx):
print("{0}\tnum={1}".format(threading.current_thread().getName(), num))
delay = math.ceil(random.random()*2)
time.sleep(delay)
if __name__ == '__main__':
th1 = threading.Thread(target=printNum, args=(2,), name="thread1")
th2 = threading.Thread(target=printNum, args=(3,), name="thread2")
th1.start()
th2.start()
th1.join()
th2.join()
print("{0} 线程结束".format(threading.current_thread().getName()))</code>2) Subclassing threading.Thread – override the run() method to define thread behavior.
<code>import threading, time, random, math
class MultiThread(threading.Thread):
def __init__(self, threadName, num):
super().__init__(name=threadName)
self.num = num
def run(self):
for i in range(self.num):
print("{0} i={1}".format(threading.current_thread().getName(), i))
delay = math.ceil(random.random()*2)
time.sleep(delay)
if __name__ == '__main__':
thr1 = MultiThread("thread1", 3)
thr2 = MultiThread("thread2", 2)
thr1.start()
thr2.start()
thr1.join()
thr2.join()
print("{0} 线程结束".format(threading.current_thread().getName()))</code>Daemon threads are explained: a daemon thread terminates automatically when the main (non‑daemon) thread finishes. Daemon status must be set before calling start() . The article shows examples where daemon threads may or may not complete before program exit.
<code>import threading, time
def run(taskName):
print("任务:", taskName)
time.sleep(2)
print("{0} 任务执行完毕".format(taskName))
if __name__ == '__main__':
for i in range(3):
thr = threading.Thread(target=run, args=(f"task-{i}",))
thr.setDaemon(True)
thr.start()
print("{0}线程结束,当线程数量={1}".format(threading.current_thread().getName(), threading.active_count()))
print("消耗时间:", time.time() - start_time)</code>The article then discusses thread‑safety problems caused by shared global variables and demonstrates how race conditions can produce incorrect results when multiple threads modify the same variable without protection.
<code>balance = 100
def change(num, counter):
global balance
for _ in range(counter):
balance += num
balance -= num
if balance != 100:
print("balance=%d" % balance)
break
if __name__ == '__main__':
t1 = threading.Thread(target=change, args=(100, 500000), name='t1')
t2 = threading.Thread(target=change, args=(100, 500000), name='t2')
t1.start(); t2.start(); t1.join(); t2.join()
print("{0} 线程结束".format(threading.current_thread().getName()))</code>To resolve these issues, a mutex lock is introduced. The lock is created with threading.Lock() , and the critical section (the code that modifies the shared variable) is protected by lock.acquire() and lock.release() . This ensures only one thread can modify the variable at a time, preventing data inconsistency and deadlocks.
<code># 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 关键代码
...
# 释放
mutex.release()</code>Finally, the article emphasizes that proper lock usage is essential for thread safety, and that locks should be held only for the minimal necessary duration to avoid performance bottlenecks.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.