Python之多线程

一、使用threading模块的Thread类

1.1 介绍

  这是 Python 中最基本的创建线程的方法。通过定义一个函数,然后将这个函数作为参数传递给Thread类的构造函数来创建线程。每个线程对象代表一个独立的执行线程。

1.2 代码示例

import threading
import time

def print_numbers():
    for i in range(1, 6):
        print(i)
        time.sleep(1)

def print_letters():
    for letter in 'abcde':
        print(letter)
        time.sleep(1)

t1 = threading.Thread(target = print_numbers)
t2 = threading.Thread(target = print_letters)

t1.start()
t2.start()

t1.join()
t2.join()

1.3 优点

  简单直观,易于理解和使用。对于初学者来说,这种方式很容易上手。可以方便地控制每个线程的执行,如启动(start)和等待线程结束(join)。简单易用,适合轻量级的线程管理。

1.4 缺点

  当需要创建大量线程时,代码可能会变得冗长。由于全局解释器锁(GIL)的存在,在CPU密集型任务中,多线程可能不会带来性能上的提升。

二、继承threading.Thread类

2.1 介绍

  通过创建一个新的类,继承自threading.Thread类,然后重写run方法。在run方法中定义线程要执行的任务。这样创建的类的实例就是一个线程对象。

2.2 代码示例

import threading
import time

class PrintNumbersThread(threading.Thread):
    def run(self):
        for i in range(1, 6):
            print(i)
            time.sleep(1)

class PrintLettersThread(threading.Thread):
    def run(self):
        for letter in 'abcde':
            print(letter)
            time.sleep(1)

t1 = PrintNumbersThread()
t2 = PrintLettersThread()

t1.start()
t2.start()

t1.join()
t2.join()

2.3 优点

  对于复杂的线程任务,可以将相关的代码封装在一个类中,使代码结构更加清晰。可以方便地在类中定义属性和方法,方便线程之间的交互和数据共享(通过类的属性)。

2.4 缺点

  相对于第一种方法,代码量可能会更多,因为需要创建一个新的类。继承关系可能会使代码的逻辑变得稍微复杂一些,特别是对于不熟悉面向对象编程的开发者。

三、使用concurrent.futures模块中的ThreadPoolExecutor

3.1 介绍

  这个模块提供了一个高级的接口来管理线程池。ThreadPoolExecutor可以自动管理线程的创建、复用和销毁。可以通过提交任务(函数及其参数)到线程池来并发执行任务。

3.2 代码示例

import concurrent.futures
import time

def print_numbers():
    for i in range(1, 6):
        print(i)
        time.sleep(1)

def print_letters():
    for letter in 'abcde':
        print(letter)
        time.sleep(1)

with concurrent.futures.ThreadPoolExecutor(max_workers = 2) as executor:
    future1 = executor.submit(print_numbers)
    future2 = executor.submit(print_letters)
    futures = [future1, future2]
    for future in concurrent.futures.as_completed(futures):
        try:
            future.result()
        except Exception as e:
            print("An error occurred:", e)

3.3 优点

  它可以有效地管理线程资源,避免了频繁地创建和销毁线程的开销,尤其适用于需要执行大量短任务的情况。可以方便地获取任务的执行结果,并且可以处理任务执行过程中的异常。提供了更高级的接口,易于管理多个线程,适用于多种并发任务。

3.4 缺点

  对于简单的场景,可能会觉得使用ThreadPoolExecutor有些过于复杂。如果对线程池的大小(max_workers)设置不合理,可能会影响性能。例如设置得过大可能会导致资源竞争,设置得过小可能无法充分利用多核处理器。同样受GIL限制,适合I/O密集型任务。

四、使用multiprocessing模块

4.1 介绍

  虽然这个模块主要用于创建多进程,但也可以用来创建多线程。通过Process类创建进程时,可以设置daemon=True,这样进程就会在后台运行,类似于线程。

4.2 代码示例

from multiprocessing import Process

def task(name):
    print(f"Process {name}: starting")
    for i in range(5):
        print(f"Process {name}: {i}")
    print(f"Process {name}: finishing")

# 创建进程
process1 = Process(target=task, args=("A",), daemon=True)
process2 = Process(target=task, args=("B",), daemon=True)

# 启动进程
process1.start()
process2.start()

# 等待进程完成
process1.join()
process2.join()

4.3 优点

  可以绕过GIL,适用于CPU密集型任务。

4.4 缺点

  进程间通信比线程间通信更复杂,资源消耗也更大。

五、使用第三方库

  例如threadpool、greenlet等,这些库提供了更高级的线程管理功能。

总结

  每种方法都有其适用场景。对于I/O密集型任务,threading和concurrent.futures是不错的选择;而对于CPU密集型任务,则可能需要考虑使用multiprocessing或第三方库。选择哪种方法取决于具体的应用需求和性能考量。

posted @ 2024-10-28 13:18  NLP的小Y  阅读(59)  评论(0编辑  收藏  举报
/*粒子线条,鼠标移动会以鼠标为中心吸附的特效*/