python:并发编程(八)

前言

本文将和大家一起探讨python的多线程并发编程(中篇),使用内置基本库threading来实现并发,先通过官方来简单使用这个模块。先打好基础,能够有个基本的用法与认知,后续文章,我们再进行详细使用。

本文为python并发编程的第八篇,上一篇文章地址如下:

python:并发编程(七)_Lion King的博客-CSDN博客

下一篇文章地址如下:

python:并发编程(九)_Lion King的博客-CSDN博客

一、线程基础编程

1、信号量对象

在Python的threading模块中,可以使用Semaphore对象来实现信号量。信号量是一种用于控制并发访问资源的机制,它允许多个线程同时访问资源,但限制了同时访问的线程数量。

下面是一个使用Semaphore对象的示例:

import threading

# 创建信号量对象,初始值为3
semaphore = threading.Semaphore(3)

# 定义线程函数
def worker():
    # 获取信号量,如果没有可用的信号量,则阻塞等待
    semaphore.acquire()
    try:
        # 访问共享资源
        print("Thread {} is accessing the resource.".format(threading.current_thread().name))
    finally:
        # 释放信号量
        semaphore.release()

# 创建多个线程并启动
for i in range(5):
    t = threading.Thread(target=worker)
    t.start()

在上述代码中,我们创建了一个初始值为3的信号量对象semaphore。然后定义了一个worker函数作为线程的执行函数。在worker函数中,线程首先会调用semaphore.acquire()来获取信号量,如果当前没有可用的信号量,则线程会被阻塞等待。然后线程执行一些操作,模拟对共享资源的访问。最后,线程调用semaphore.release()释放信号量,以便其他线程可以获取。

在上述示例中,我们创建了5个线程,但由于信号量的初始值为3,因此最多只能有3个线程同时访问共享资源,其他线程将被阻塞等待,直到有可用的信号量。

使用信号量可以有效控制并发访问资源的线程数量,从而实现对共享资源的合理管理和调度。

2、事件对象

在Python的threading模块中,可以使用Event对象来实现线程间的事件通知机制。Event对象可以用于线程之间的同步,一个线程可以等待另一个线程触发事件,从而实现线程间的协调和通信。

下面是一个使用Event对象的示例:

import threading
import time

# 创建事件对象
event = threading.Event()

# 定义线程函数
def worker():
    print("Worker is waiting for the event.")
    # 等待事件触发
    event.wait()
    print("Worker has received the event.")

# 创建线程并启动
t = threading.Thread(target=worker)
t.start()

# 主线程等待一段时间后触发事件
time.sleep(3)
print("Main thread is setting the event.")
# 触发事件,唤醒等待的线程
event.set()

# 等待子线程执行完成
t.join()

在上述代码中,我们创建了一个Event对象event。然后定义了一个worker函数作为线程的执行函数,在该函数中,线程会等待事件的触发,即调用event.wait()进行等待。在主线程中,我们等待一段时间后触发事件,即调用event.set()来设置事件。这样就会唤醒等待的线程,使其继续执行。

运行上述代码,可以看到主线程先打印一段信息后设置了事件,然后等待一段时间后,子线程才接收到事件并打印相应的信息。

Event对象提供了以下几个常用的方法:

(1)wait(timeout=None):等待事件的触发,如果事件已触发则立即返回,如果设置了超时时间,则等待指定的时间后返回。

(2)set():设置事件,将事件状态设置为已触发,唤醒等待的线程。

(3)clear():重置事件状态,将事件设置为未触发的状态。

(4)is_set():判断事件是否已触发,返回一个布尔值。

通过使用Event对象,我们可以实现线程间的事件通知和同步,使线程能够在适当的时机等待事件或响应事件的触发。这样可以更好地控制线程的执行顺序和协作方式。

3、定时器对象

在Python的threading模块中,可以使用Timer对象来实现定时器功能。Timer对象允许您在指定的时间间隔后触发一个函数的执行。

下面是一个使用Timer对象的示例:

import threading

# 定时器函数
def timer_function():
    print("Timer expired!")

# 创建定时器对象,设置定时时间为5秒,并指定定时器函数
timer = threading.Timer(5, timer_function)

# 启动定时器
timer.start()

# 主线程继续执行其他操作
print("Main thread continues...")

# 等待定时器执行完成
timer.join()

在上述代码中,我们创建了一个Timer对象timer,并设置定时时间为5秒。然后指定了定时器函数timer_function,该函数会在定时器到期时被调用并执行相应的操作。我们启动定时器后,主线程继续执行其他操作,并打印相应的信息。当定时器到期时,定时器函数会被触发并执行。

运行上述代码,可以看到主线程先打印一段信息后继续执行其他操作,而定时器在指定的时间间隔后触发,执行相应的操作。

Timer对象提供了以下几个常用的方法和属性:

(1)start():启动定时器,开始计时。

(2)cancel():取消定时器,终止定时器的计时过程。

(3)is_alive():判断定时器是否正在运行,返回一个布尔值。

(4)join(timeout=None):等待定时器执行完成,如果设置了超时时间,则等待指定的时间后返回。

通过使用Timer对象,我们可以实现定时执行某个函数的功能,非常适用于需要定时执行任务的场景,如定时检查某个状态、定时发送消息等。

4、栅栏对象

在Python的threading模块中,栅栏(Barrier)对象可以用于协调多个线程,在达到指定数量的线程之前,阻塞线程的执行,一旦达到指定数量的线程,所有被阻塞的线程将同时释放并继续执行。

下面是一个使用栅栏对象的示例:

import threading

# 创建栅栏对象,指定线程数量为3
barrier = threading.Barrier(3)

# 线程函数
def worker():
    print("Worker thread started.")
    barrier.wait()  # 等待栅栏释放
    print("Worker thread released.")

# 创建并启动多个线程
threads = []
for _ in range(3):
    thread = threading.Thread(target=worker)
    threads.append(thread)
    thread.start()

# 等待所有线程执行完成
for thread in threads:
    thread.join()

print("All threads completed.")

在上述代码中,我们创建了一个栅栏对象barrier,并指定线程数量为3。然后定义了线程函数worker,在该函数中,线程首先打印一条开始信息,然后调用barrier.wait()等待栅栏的释放,最后打印一条释放信息。我们创建了3个线程,并启动它们。这些线程会在栅栏对象上等待,直到所有线程都到达栅栏位置,栅栏对象将释放所有线程,它们同时继续执行。最后,主线程等待所有线程执行完成,并打印一条完成信息。

运行上述代码,可以看到3个线程首先输出开始信息,然后阻塞在栅栏位置处,当所有线程都到达栅栏位置时,栅栏释放,所有线程同时继续执行,最后主线程等待所有线程执行完成并打印完成信息。

栅栏对象提供了以下几个常用的方法和属性:

  • wait(timeout=None):线程调用该方法等待栅栏释放,如果指定了超时时间,则等待指定的时间后返回。被阻塞的线程数量减一。
  • reset():重置栅栏对象,将栅栏恢复到初始状态。
  • n_waiting:返回当前等待栅栏释放的线程数量。
  • parties:返回栅栏对象设置的线程数量。

栅栏对象在需要协调多个线程在某个点上同时执行的情况下非常有用,可以控制线程的同步和并发。

5、在 with 语句中使用锁、条件和信号量

threading模块中,锁(Lock)、条件(Condition)和信号量(Semaphore)都可以在with语句中使用。使用with语句可以确保在退出with块时自动释放相关资源,避免了手动调用锁、条件和信号量的释放方法,提高了代码的可读性和健壮性。

下面是在with语句中使用锁、条件和信号量的示例:

(1)使用锁(Lock):

import threading

# 创建锁对象
lock = threading.Lock()

# 在with语句中使用锁
with lock:
    # 执行需要保护的临界区代码
    print("Critical section")
    # ...

(2)使用条件(Condition)

详见上一张“条件对象”

(3)使用信号量(Semaphore)

import threading

# 创建信号量对象
semaphore = threading.Semaphore()

# 在with语句中使用信号量
with semaphore:
    # 执行需要限制并发数量的代码
    print("Critical section")
    # ...

你可能感兴趣的:(python,python)