CodeGym /课程 /Python SELF ZH /threading 模块

threading 模块

Python SELF ZH
第 25 级 , 课程 1
可用

2.1 模块 threading

Python中的多线程是同时运行多个线程 (threads)的一种方式,这样可以更有效地利用CPU资源,特别是在I/O操作或其他可以并行执行的任务中。

Python多线程的基本概念:

线程是一个最小的执行单元,可以与同一进程中的其他线程并行工作。一个进程中的所有线程共享内存,这使得线程之间的数据交换成为可能。

进程是操作系统中执行的程序实例,拥有自己的地址空间和资源。与线程不同,进程相互隔离,通过进程间通信 (IPC)进行数据交换。

GIL 是Python解释器中的机制,防止多个Python线程同时执行。GIL确保Python代码执行的安全性,但限制了多核处理器上多线程程序的性能。

重要! 请注意,由于Global Interpreter Lock (GIL),Python中的多线程可能无法显著提高对计算密集型任务的性能,因为GIL阻止多个Python线程在多核处理器上同时执行。

模块 threading

Python中的模块 threading提供了用于线程操作的高级接口。它允许创建和管理线程,进行线程同步及其之间的交互。让我们更详细地了解这个模块的关键组件和功能。

模块 threading 的主要组件

用于线程操作的实体:

  • Thread — 创建和管理线程的主要类。
  • Timer — 用于在指定时间后执行函数的计时器。
  • ThreadLocal — 创建线程本地数据。

线程同步机制:

  • Lock — 用于防止同时访问共享资源的同步原语。
  • Condition — 用于更复杂的线程同步的条件变量。
  • Event — 用于线程之间通知的原语。
  • Semaphore — 限制可以同时执行某一部分的线程数量的原语。
  • Barrier — 同步设定数量的线程,阻塞它们直到所有的线程到达屏障。

下面我会讲解3个用于线程操作的类,线程同步机制你暂时用不到。

2.2 类 Thread

Thread 是用于创建和管理线程的主要类。它有4个主要方法:

  • start(): 启动线程执行。
  • join(): 当前线程暂停并等待已启动线程完成。
  • is_alive(): 如果线程正在执行则返回True
  • run(): 包含将在线程中执行的代码的方法。在从类 Thread 继承时被重写。

比想象的要简单得多——以下是使用类 Thread 的示例。

启动简单线程


import threading

def worker():
    print("Worker thread is running")
            
# 创建新线程
t = threading.Thread(target=worker) #创建了一个新的线程对象
t.start() #启动线程
t.join() # 等待线程结束
print("Main thread is finished")
        

调用 start 方法后,worker 函数将开始执行。更准确地说,它的线程将被添加到活动线程列表中。

使用参数


import threading

def worker(number, text):
    print(f"Worker {number}: {text}")
            
# 创建带参数的新线程
t = threading.Thread(target=worker, args=(1, "Hello"))
t.start()
t.join()
        

为新线程传递参数,只需将它们作为元组指定并赋值给参数args。当调用 target 中指定的函数时,参数将自动传递。

重写 run 方法


import threading

class MyThread(threading.Thread):
    def run(self):
        print("Custom thread is running")
            
# 创建并启动线程
t = MyThread()
t.start()
t.join()
        

有两种方法指定函数来开始新的线程执行——可以在创建 Thread 对象时通过 target 参数传递,也可以从类 Thread 继承并重写 run 函数。两种方式都被认为是合法的并且经常使用。

2.3 类 Timer

模块 threading 中的类 Timer 用于在指定时间后启动一个函数。这个类对于在多线程环境中执行延迟任务非常有用。

计时器通过要调用的函数和延迟时间(以秒为单位)创建和初始化。

  • 方法 start() 启动计时器,计时器在计时指定的时间后调用指定的函数。
  • 方法 cancel() 可以在计时器触发前停止计时器。这对于防止不需要的函数调用很有用。

使用示例:

启动带延迟的函数

在这个示例中,函数 hello 会在计时器启动后5秒被调用。


import threading

def hello():
    print("Hello, world!")
            
# 创建计时器,5秒后调用hello函数
t = threading.Timer(5.0, hello)
t.start()  # 启动计时器
        

在执行前停止计时器

这里计时器将在 hello 函数执行之前被停止,因此不会有任何输出。


import threading

def hello():
    print("Hello, world!")
            
# 创建计时器
t = threading.Timer(5.0, hello)
t.start()  # 启动计时器
            
# 在执行前停止计时器
t.cancel()
        

带参数的计时器

在这个示例中,计时器将在3秒后调用函数 greet 并传递参数"Alice"


import threading

def greet(name):
    print(f"Hello, {name}!")
            
# 创建带参数的计时器
t = threading.Timer(3.0, greet, args=["Alice"])
t.start()
        

Timer 对于计划在特定时间后任务的执行非常有用。然而,计时器不保证绝对精确的执行时间,因为这取决于系统负载和线程调度器的操作。

2.4 类 ThreadLocal

ThreadLocal 旨在创建具有自己本地数据的线程。这在多线程应用中非常有用,当每个线程需要自己的版本数据以避免冲突和同步问题时。

每个使用 ThreadLocal 的线程将拥有自己的独立数据副本。存储在 ThreadLocal 对象中的数据对每个线程都是唯一的,并且不会与其他线程共享。这对于存储仅在单个线程上下文中使用的数据非常方便,例如web应用中的当前用户或数据库的当前连接。

使用示例:

基本使用

在这个示例中,每个线程将其自己的名字分配给本地变量 value 并输出。value 的值对每个线程都是唯一的。


import threading

# 创建 ThreadLocal 对象
local_data = threading.local()
            
def process_data():
    # 分配线程本地变量的值
    local_data.value = threading.current_thread().name
    # 访问线程本地变量
    print(f'Value in {threading.current_thread().name}: {local_data.value}')

threads = []
for i in range(5):
    t = threading.Thread(target=process_data)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

在Web应用中存储用户数据

在这个示例中,每个线程处理其自己的用户请求。user_data.user 的值对每个线程是唯一的。


import threading
# 创建 ThreadLocal 对象
user_data = threading.local()
def process_request(user):
    # 分配线程本地变量的值
    user_data.user = user
    handle_request()

def handle_request():
    # 访问线程本地变量
    print(f'Handling request for user: {user_data.user}')

threads = []
users = ['Alice', 'Bob', 'Charlie']
for user in users:
    t = threading.Thread(target=process_request, args=(user,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

这就是 threading 库中最有用的3个类。你很可能会在工作中使用它们,而其他类就不太可能使用了。现在大家都转向了异步函数和asyncio。我们接下来会一直谈论这个。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION