Python入门系列之八-Python中的多线程与异步编程*

人是为活着本身而活着的,而不是为了活着之外的任何事物所活着


Python作为一种高层次的编程语言,提供了多种并发和并行处理的方式,帮助我们在需要处理I/O操作或执行计算密集型任务时提高效率。在Python中,主要通过多线程(threading模块)、异步编程(asyncio库)、以及协程(asyncawait语法)来实现并发。本文将带你深入了解这些技术,并比较它们在不同场景下的适用性。

1. 多线程:threading模块

多线程是通过同时运行多个线程来执行任务的技术。每个线程是一个独立的执行流,通常它们共享相同的内存空间。Python通过threading模块实现多线程编程。

1.1. 基本概念
  • 线程:是程序执行的最小单位。多个线程共享同一进程的资源。
  • 并发:多个线程在同一时刻执行不同的任务,通常是为了提高程序的响应性。
  • 并行:多个线程在多个CPU核心上同时执行任务。
1.2. threading模块基础

threading模块提供了一个简单的方式来创建和管理线程。下面是一个简单的多线程例子:

import threading
import time

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

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

# 创建线程
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)

# 启动线程
thread1.start()
thread2.start()

# 等待线程结束
thread1.join()
thread2.join()

print("Finished")

在这个例子中,print_numbersprint_letters会在不同的线程中并发执行。每个线程都会同时执行自己的任务,两个线程的执行时间重叠。

1.3. 注意事项
  • GIL(Global Interpreter Lock):Python的CPython实现采用了全局解释器锁(GIL),它确保同一时间只有一个线程可以执行Python字节码。因此,threading模块在计算密集型任务上并不一定能提高性能,适合I/O密集型任务(如文件操作、网络请求)。
  • 线程安全:多线程编程中需要特别注意线程之间的资源共享问题,避免竞态条件和数据一致性问题。

2. 异步编程:asyncio

异步编程(或非阻塞编程)是一种通过事件循环来处理任务的方式,它可以在执行某些操作时让程序继续执行其他任务,而无需等待。这种方式特别适用于I/O密集型任务。

2.1. asyncio概述

asyncio是Python标准库中的一个模块,支持异步编程。它提供了协程、事件循环和异步任务等工具,允许程序在等待I/O操作(如网络请求、磁盘操作)时执行其他任务。

2.2. 基本概念
  • 协程(Coroutine):是一个Python函数,它通过async def定义,并能在执行过程中挂起(暂停执行),允许其他协程执行。
  • 事件循环(Event Loop):是异步编程的核心。它负责管理和调度任务,确保异步任务的执行顺序和调度。
  • asyncawaitasync用于定义协程,await用于挂起协程的执行,直到某个异步操作完成。
2.3. 示例:使用asyncio
import asyncio

async def fetch_data():
    print("Start fetching data")
    await asyncio.sleep(2)  # 模拟I/O操作
    print("Data fetched")
    return "Data"

async def process_data():
    print("Start processing data")
    await asyncio.sleep(1)  # 模拟I/O操作
    print("Data processed")
    return "Processed Data"

async def main():
    # 并行执行fetch_data和process_data
    data = await asyncio.gather(fetch_data(), process_data())
    print(data)

# 运行事件循环
asyncio.run(main())

在上面的例子中,fetch_dataprocess_data是两个协程,它们可以并行执行,而不会因为等待asyncio.sleep而阻塞程序。asyncio.gather用于并行执行多个协程。

2.4. 优势与应用场景
  • I/O密集型任务asyncio适用于处理大量I/O操作(如网络请求、文件读写等),可以显著提高程序的响应性。
  • 单线程高效执行asyncio不依赖多个线程,而是在单个线程中通过事件循环实现异步执行,因此能有效节省内存和上下文切换开销。

3. 协程:asyncawait语法

协程是Python中的一种特殊函数,它允许在执行过程中被挂起(暂停),并在稍后恢复执行。协程通常通过async def定义,并使用await关键字等待某个异步操作的结果。

3.1. async defawait 语法
  • async def:用于定义协程函数。
  • await:在协程中用于等待另一个协程执行结果,并挂起当前协程的执行。
3.2. 示例:协程与async/await
import asyncio

async def task1():
    print("Task 1 starts")
    await asyncio.sleep(2)
    print("Task 1 ends")
    return "Result from Task 1"

async def task2():
    print("Task 2 starts")
    await asyncio.sleep(1)
    print("Task 2 ends")
    return "Result from Task 2"

async def main():
    result1 = await task1()
    result2 = await task2()
    print(result1)
    print(result2)

asyncio.run(main())

在这个例子中,task1task2都是协程函数。由于await会挂起当前协程的执行,因此这两个任务可以并行执行。

3.3. 协程的优势
  • 非阻塞:协程能够在I/O操作(如网络请求、磁盘读写等)期间释放控制权,使得其他协程可以在等待时执行。
  • 轻量级:相比线程,协程更轻量,创建和销毁的开销较小,因此适用于大规模的并发任务。

4. Python中的并发与并行

  • 并发(Concurrency):并发意味着在同一时间段内有多个任务在进行,但并不一定是同时执行的。在Python中,线程和协程可以实现并发执行。
  • 并行(Parallelism):并行指的是在同一时刻有多个任务同时执行,通常是多核处理器实现的。Python的multiprocessing模块可以用来实现真正的并行计算,它不会受到GIL的限制。
4.1. 比较多线程和异步编程
特性多线程异步编程(asyncio)
执行方式多个线程同时执行任务在单线程内通过事件循环执行任务
适用场景I/O密集型任务,有限的并行计算I/O密集型任务
GIL影响受到GIL影响,计算密集型任务效率较低不受GIL影响,适合I/O密集型任务
内存开销每个线程都有独立的内存空间所有协程共享内存,内存开销小
复杂度线程同步、竞态条件等问题较多事件循环与协程设计较简单

5. 总结

  • 多线程适合I/O密集型任务,但由于GIL的存在,它在计算密集型任务中的效果有限。
  • 异步编程asyncio)通过事件循环和协程实现非阻塞I/O,适合高并发的I/O密集型任务,如网络请求和数据库查询等。
  • 协程提供了更高效、更轻量的并发方式,通过asyncawait语法,使得代码更具可读性。
  • 并行计算则适用于计算密集型任务,Python的multiprocessing模块可以绕过GIL限制,使用多个CPU核心实现真正的并行计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JM_life

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值