目录

  • Python快速上手(二十三)
  • Python3 多线程
  • 1. 线程的创建
  • 2. 线程同步
  • 2.1 锁(Lock)
  • 2.2 信号量(Semaphore)
  • 2.3 事件(Event)
  • 2.4 条件(Condition)
  • 3. 线程优先级队列(Queue)
  • 总结


Python快速上手(二十三)

Python3 多线程

多线程类似于同时执行多个不同程序,多线程运行有如下优点:

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
  • 程序的运行速度可能加快。
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
  • 多线程是一种并发编程的技术,允许程序同时执行多个线程。在Python中,我们可以使用threading模块来实现多线程编程。

Python3 线程中常用的两个模块为:

  • _thread
  • threading(推荐使用)
    thread 模块已被废弃。可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 “_thread”。在本文中,我们将详细讲解Python3中的多线程编程,包括线程的创建、同步、优先级队列等内容。

1. 线程的创建

在Python中,可以通过创建Thread类的实例来创建线程。以下是一个简单的示例代码:

#!/usr/bin/python3

import threading
import time

exitFlag = 0

class myThread (threading.Thread):
    def __init__(self, threadID, name, delay):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.delay = delay
    def run(self):
        print ("开始线程:" + self.name)
        print_time(self.name, self.delay, 5)
        print ("退出线程:" + self.name)

def print_time(threadName, delay, counter):
    while counter:
        if exitFlag:
            threadName.exit()
        time.sleep(delay)
        print ("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1

# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 开启新线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("退出主线程")

以上程序执行结果如下;

开始线程:Thread-1
开始线程:Thread-2
Thread-1: Wed Jan  5 17:34:54 2022
Thread-2: Wed Jan  5 17:34:55 2022
Thread-1: Wed Jan  5 17:34:55 2022
Thread-1: Wed Jan  5 17:34:56 2022
Thread-2: Wed Jan  5 17:34:57 2022
Thread-1: Wed Jan  5 17:34:57 2022
Thread-1: Wed Jan  5 17:34:58 2022
退出线程:Thread-1
Thread-2: Wed Jan  5 17:34:59 2022
Thread-2: Wed Jan  5 17:35:01 2022
Thread-2: Wed Jan  5 17:35:03 2022
退出线程:Thread-2
退出主线程

2. 线程同步

在多线程编程中,线程同步是非常重要的,它用于确保多个线程之间的数据访问和操作是安全的,避免出现竞争条件和数据不一致的情况。Python提供了多种线程同步机制,包括锁、信号量、事件、条件等。

2.1 锁(Lock)

锁是最基本的线程同步机制,通过获取锁可以阻止其他线程访问共享资源,从而确保数据的一致性。在Python中,可以使用threading.Lock类来创建锁对象,并通过acquire()和release()方法来获取和释放锁。

import threading

lock = threading.Lock()

def increment_counter():
    global counter
    lock.acquire()
    counter += 1
    lock.release()

在上面的示例中,我们创建了一个锁对象lock,然后在increment_counter函数中使用acquire()方法获取锁,对counter变量进行操作后再通过release()方法释放锁。

2.2 信号量(Semaphore)

信号量是一种更加通用的线程同步机制,它可以控制多个线程同时访问共享资源的数量。在Python中,可以使用threading.Semaphore类来创建信号量对象,并通过acquire()和release()方法来控制资源的访问。

import threading

semaphore = threading.Semaphore(2) # 允许同时有2个线程访问共享资源

def use_resource():
    semaphore.acquire()
    # 访问共享资源
    semaphore.release()

在上面的示例中,我们创建了一个信号量对象semaphore,并设置允许同时有2个线程访问共享资源。线程在访问资源前需要先通过acquire()方法获取信号量,访问完成后再通过release()方法释放信号量。

2.3 事件(Event)

事件是一种线程同步机制,用于实现线程之间的通信和协调。在Python中,可以使用threading.Event类来创建事件对象,并通过set()和clear()方法来设置和清除事件。

import threading

event = threading.Event()

def wait_for_event():
    event.wait() # 等待事件被设置
    # 处理事件

def set_event():
    event.set() # 设置事件

在上面的示例中,我们创建了一个事件对象event,线程可以通过wait()方法等待事件被设置,通过set()方法设置事件,从而唤醒等待事件的线程。

2.4 条件(Condition)

条件是一种复杂的线程同步机制,它可以让线程在特定条件下等待或唤醒。在Python中,可以使用threading.Condition类来创建条件对象,并通过wait()、notify()和notify_all()方法来实现线程的等待和唤醒。

import threading

condition = threading.Condition()

def wait_for_condition():
    with condition:
        condition.wait() # 等待条件
        # 处理条件

def set_condition():
    with condition:
        condition.notify() # 唤醒等待条件的线程

在上面的示例中,我们创建了一个条件对象condition,线程可以通过wait()方法等待条件,通过notify()方法唤醒等待条件的线程。

3. 线程优先级队列(Queue)

在Python中,线程优先级队列(Priority Queue)是一种特殊的队列,它可以根据元素的优先级来确定元素的顺序。在标准的队列中,元素是按照先进先出(FIFO)的顺序进行排列的,而在优先级队列中,元素的顺序是根据其优先级来确定的,优先级高的元素会被优先处理。Python中的线程优先级队列通常使用queue.PriorityQueue类来实现,它是线程安全的队列,可以在多线程环境中安全地进行操作。
Queue 模块中的常用方法:

  • Queue.qsize() 返回队列的大小
  • Queue.empty() 如果队列为空,返回True,反之False
  • Queue.full() 如果队列满了,返回True,反之False
  • Queue.full 与 maxsize 大小对应
  • Queue.get([block[, timeout]])获取队列,timeout等待时间
  • Queue.get_nowait() 相当Queue.get(False)
  • Queue.put(item) 写入队列,timeout等待时间
  • Queue.put_nowait(item) 相当Queue.put(item, False)
  • Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
  • Queue.join() 实际上意味着等到队列为空,再执行别的操作
import queue
import threading
import time

def process_task(priority_queue):
    while True:
        priority, data = priority_queue.get()
        print(f'Processing task: {data}, Priority: {priority}')
        priority_queue.task_done()

priority_queue = queue.PriorityQueue()

# 启动处理任务的线程
thread = threading.Thread(target=process_task, args=(priority_queue,))
thread.start()

# 插入任务到优先级队列
priority_queue.put((2, 'Task 1'))
priority_queue.put((1, 'Task 2'))
priority_queue.put((3, 'Task 3'))

# 等待所有任务处理完成
priority_queue.join()

在上面的示例中,我们创建了一个优先级队列priority_queue,并启动了一个线程来处理任务。然后向优先级队列中插入了三个任务,并通过join()方法等待所有任务处理完成。

总结

本文详细讲解了Python3中的多线程编程,包括线程的创建、同步、优先级队列等内容。通过合理地使用多线程技术,可以提高程序的执行效率,实现并发处理任务。