python中线程和协程

本文介绍Python中的线程和协程实现方式及应用。通过示例代码展示了threading模块如何创建和管理线程,以及gevent库如何实现协程并发处理。协程能够有效避免全局解释器锁(GIL)带来的限制。

python中线程和协程

1 线程

Python实现线程,使用threading模块实现。start方法是启动线程;join方法是等待当前线程。

import threading
import time
import random

# 线程A
def thread_a():
    for i in range(3):
        print("线程A--%d" % i)
        # 随机睡眠1-4秒
        time.sleep(random.randint(1, 4))


# 线程B
def thread_b():
    for i in range(3):
        print("线程B--%d" % i)
        time.sleep(random.randint(1, 4))


if __name__ == '__main__':
    # 参数target是线程执行的函数名
    my_thread_a = threading.Thread(target=thread_a)
    my_thread_b = threading.Thread(target=thread_b)

    # 启动线程A
    my_thread_a.start()
    # 等待my_thread_a执行完成后,再继续执行
    # my_thread_a.join()

    # 启动线程B
    my_thread_b.start()

    print("主线程")

2 协程

由于python受到全局解释器锁(Global Interpreter Lock, GIL)的限制,任何时候只能有一个thread处于运行状态。因此,python的单线程和多线程的运行效果相差不大。因此,推荐使用协程解决并发问题。

协程(Coroutine)是单线程下的并发,又称微线程,纤程。它是一种用户态的轻量级线程,即是由用户程序自己控制调度的。

实现协程有yield(yield是python自带的手动切换协程的模块)、 greenlet(greenlet是基于C实现的半自动切换协程的模块)、gevent(gevent是基于greenlet实现的自动切换协程的模块)三种方法实现协程。此处主要使用gevent实现协程。
实现协程之间的锁,可以使用 BoundedSemaphore,可以使用 acquire、 release方法实现锁。

# 安装gevent
# pip install gevent

import gevent
from gevent import monkey

import time
import random

# 必须添加“猴子补丁”,否则不能自动切换协程
monkey.patch_all()


def gevent_a(n):
    for i in range(n):
        print("协程A--%d" % i)
        time.sleep(random.randint(1, 4))


def gevent_b(n):
    for i in range(n):
        print("协程B--%d" % i)
        # time.sleep(random.randint(1, 4))


if __name__ == '__main__':
    # 创建协程对象
    my_gevent_a = gevent.spawn(gevent_a, 4)
    my_gevent_a.run()
    my_gevent_b = gevent.spawn(gevent_b, 4)
    my_gevent_b.run()
    

    # 阻塞主线程,等待协程。此处需要阻塞主线程,等待协程
    my_gevent_a.join()
    my_gevent_b.join()

    print("主线程")
### Python 中多线程协程的区别及使用场景 #### 一、基本概念对比 多线程协程都是用于处理并发任务的技术,但在实现机制上存在显著差异。 - **多线程**是指操作系统级别的并行技术。每个线程都有独立的栈空间,能够同时运行多个任务[^1]。然而,在 Python 中由于 GIL(Global Interpreter Lock)的存在,同一时刻只有一个线程能执行 Python 字节码,因此多线程在 CPU 密集型任务中的性能提升有限[^4]。 - **协程**是一种用户态下的轻量级线程,它不依赖于操作系统的支持,而是由程序员手动控制其调度[^3]。协程通过协作的方式共享同一个线程资源,避免了线程切换带来的开销[^2]。 #### 二、主要区别 | 特性 | 多线程 | 协程 | |-----------------|-----------------------------------------|------------------------------------------| | 资源消耗 | 高 | 极低 | | 上下文切换 | 存在线程间上下文切换 | 不涉及上下文切换 | | 并发能力 | 受限于硬件和 GIL | 更高的并发能力 | | 编程复杂度 | 较高 | 较低 | #### 三、适用场景分析 ##### (1)I/O 密集型任务 对于 I/O 密集型任务(如文件读写、网络请求),协程表现更优。因为这类任务大部分时间都花费在等待外部设备响应上,而协程可以在等待期间快速切换到其他任务,从而提高整体效率。 ```python import asyncio async def fetch_data(): print("Start fetching") await asyncio.sleep(2) # 模拟异步IO操作 print("Done fetching") return {"data": 1} async def main(): task = asyncio.create_task(fetch_data()) data = await task print(data) # 运行事件循环 asyncio.run(main()) ``` ##### (2)CPU 密集型任务 针对 CPU 密集型任务(如大量数值运算),推荐使用多线程或多进程来绕过 GIL 的限制。虽然理论上也可以尝试用协程完成此类工作,但由于缺乏真正的并行计算优势,实际效果往往不如预期。 ```python from concurrent.futures import ThreadPoolExecutor, as_completed def cpu_bound_operation(x): result = sum(i * i for i in range(x)) return result with ThreadPoolExecutor() as executor: futures = [executor.submit(cpu_bound_operation, num) for num in (10_000, 20_000)] for future in as_completed(futures): print(future.result()) ``` #### 四、总结建议 当面对具体项目需求时,应综合考虑任务性质以及目标平台特性等因素决定采用何种方案。如果应用程序主要是进行大量的数据交互或者服务端开发,则优先选用基于协程框架的设计;而对于科学计算等领域则更适合引入多线程甚至分布式架构解决瓶颈问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值