博客
关于我
python基础之多线程与多进程(二)
阅读量:797 次
发布时间:2023-03-07

本文共 3245 字,大约阅读时间需要 10 分钟。

Python 多线程与并发编程指南

多线程与并发编程是现代软件开发中的核心技能之一,特别是在处理I/O密集型任务、网络请求、数据库操作等场景中,多线程可以显著提升性能。本文将从基础到高级探讨Python中的多线程与协程编程知识。


1. 线程基础

1.1 线程特性

线程是操作系统任务调度的基本单元,具有以下特点:

  • 线程比进程更轻量,创建和切换速度更快。
  • 线程共享同一内存空间和资源。
  • 线程间存在竞争和同步问题。

1.2 全局解释器锁(GIL)

GIL(Global Interpreter Lock)是Python解释器的关键机制,保证了在多线程环境下Python程序的正确执行。GIL通过锁机制实现线程间的互斥,确保在同一时刻只有一个线程执行Python代码。

1.3 线程安全

在多线程环境中,共享资源可能引发竞争和并发访问问题。为了保证线程安全,通常需要使用同步锁(如threading.Lock)和互斥机制。


2. 同步锁

2.1 锁的作用

锁用于保护共享资源,确保在多线程环境下资源只能被一个线程占用。常见类型包括:

  • 互斥锁(threading.Lock:基于GIL实现的粗粒度锁。
  • 递归锁(threading.RLock:支持多次递归使用,适合递归函数。

2.2 锁的使用

import threading
lock = threading.Lock()
lock.acquire() # 加锁
try:
# 执行共享资源操作
print("获取锁成功")
finally:
lock.release() # 释放锁

2.3 死锁

死锁是多个线程因争夺资源而无法继续执行的状态。常见原因包括:

  • 互相等待对方完成的操作(如lock.acquire()lock.release()互相等待)。
  • 资源竞争未被正确同步。

2.4 递归锁

递归锁通过计数器机制支持多次锁使用,适合递归函数。

import threading
rlock = threading.RLock()
rlock.acquire()
try:
# 使用递归锁
print("获取递归锁成功")
finally:
rlock.release()

3. Event对象

3.1 Event的作用

Event对象用于线程间的同步和通信,通过设置和等待信号标志来控制线程的执行。

3.2 实例

import threading
import time
event = threading.Event()
t1 = threading.Thread(target=lambda: event.wait(2))
t1.start()
time.sleep(6)
event.set()

3.3 应用场景

Event适用于需要等待外部条件的场景,如等待Redis服务启动或数据库连接成功。


4. 信号量(Semaphore)

4.1 信号量的作用

信号量通过计数器管理资源访问次数,限制最大连接数或并发使用数量。

4.2 实例

import threading
import time
from threading import Semaphore
semaphore = Semaphore(5)
def func():
if semaphore.acquire():
print(threading.current_thread().name + "获取信号量")
time.sleep(2)
semaphore.release()
for _ in range(20):
t = threading.Thread(target=func)
t.start()

4.3 应用场景

信号量常用于限制资源连接数,如数据库连接池或服务器入口。


5. 队列(Queue)

5.1 队列实现

Python提供三种队列实现:

  • queue.Queue:FIFO,先进先出。
  • queue.LifoQueue:LIFO,先进后出。
  • queue.PriorityQueue:优先级队列,先进最小。

5.2 队列操作

import queue
q = queue.Queue()
q.put(1)
q.put(2)
print(q.qsize()) # 2
msg = q.get()
print("获取:", msg)

5.3 生产者-消费者模式

生产者将数据写入队列,消费者从队列中读取数据,通过队列实现资源的松耦合。


6. 多进程(Multiprocessing)

6.1 进程创建

使用multiprocessing.Process创建进程,适用于需要充分利用多核资源的任务。

import multiprocessing
def func():
print("hello")
p = multiprocessing.Process(target=func)
p.start()
p.join()

6.2 进程管理

支持进程的启动、join、terminate等操作,以及进程间的互斥锁、Event、Semaphore等同步机制。


7. 协程(Coroutine)

7.1 协程的优势

  • 无需线程切换,提升效率。
  • 无需锁机制,资源访问更安全。

7.2 协程实现

import time
def consumer():
r = ''
while True:
n = yield r
if not n:
break
print(f"CONSUMER: 消耗{ n }...")
time.sleep(1)
r = "200 OK"
produce = consumer()
for i in range(5):
produce.send(i)

7.3 greenlet与gevent

greenlet是协程的基础库,gevent提供了更高级别的API,支持并发网络操作。


8. gevent模块

8.1 gevent特性

  • 基于greenlet实现协程。
  • 支持并发网络操作,通过monkey patch改造标准库。
  • 提供高效的事件循环框架。

8.2 实例

from gevent import monkey, sleep
monkey.patch_all()
def f(url):
print(f"GET: {url}")
resp = request.urlopen(url)
data = resp.read()
print(f"{len(data)} bytes received from {url}")
gevent.joinall([
gevent.spawn(f, 'https://itk.org/'),
gevent.spawn(f, 'https://www.github.com/'),
gevent.spawn(f, 'https://zhihu.com/'),
])

9.扩展思考

9.1 GIL与锁的关系

GIL是Python解释器层面的锁机制,粗粒度锁(如threading.Lock)和细粒度锁(如threading.RLock)共同保障线程安全。

9.2 信号量与队列的区别

信号量用于控制资源访问次数,队列用于数据传输和缓冲,两者配合使用常见于生产者-消费者模式。


10. 总结

多线程与协程编程是提升Python应用性能的关键技术。通过合理使用锁、Event、信号量、队列等同步机制,可以在多核环境下充分发挥性能潜力。选择多线程还是多进程或协程,需根据任务特性和资源需求进行权衡。

转载地址:http://soofk.baihongyu.com/

你可能感兴趣的文章
python | xlwings,一个非常实用的 Excel 相关的 Python 库!
查看>>
python | xmltodict,一个非常厉害的 关于XML数据 Python 库!
查看>>
python | xonsh,一个超酷的 Python 库!
查看>>
python | yagmail,一个实用的 Python 库!
查看>>
python | 一文掌握Python的上下文管理器和with语句
查看>>
python | 一文看懂Python闭包机制与变量作用域规则
查看>>
python读取含中文的json
查看>>
python | 如何用Python锁避免并发错误?
查看>>
python | 提升代码迭代速度的Python重载方法
查看>>
python | 深入理解Python并发编程中的GIL限制与解决方案
查看>>
Python | 爬虫实战——亚马逊搜索页监控(附详细源码)
查看>>
python | 高效使用Python工具自动生成模块文档的秘诀
查看>>
python 一个list去除另一个list中的值
查看>>
python 三大框架的 介绍。
查看>>
Python 下载的 11 种姿势,一种比一种高级!
查看>>
python读取一个文件夹下所有图片_初学Python-找出文件夹下的所有图片
查看>>
Python 中 3 个不可思议的返回功能
查看>>
python 中 dict 的另一种用法
查看>>
Python 中 PIL 读取图片出现异常旋转的解决方法
查看>>
python读取word表格内容(1)
查看>>