Python中程序大量时间消耗在lock.acquire和time.sleep上,如何优化性能?

在执行 Profile 的时候发现大量的时间消耗在了这两个方法上,个人认为主要原因在于如下的代码:
event = threading.Event()
socket.write(data)
while request_id not in results:
event.clear()
event.wait()
result = results[request_id]
print result
result = socket.read()
results[request_id] = result
event.set()
上面代码的目的是为了把异步的网络请求转为同步请求,客户端在请求发出去之后就进入阻塞状态,直到服务端的响应返回值后,我在读取数据到 results 之后唤醒请求线程,之后请求线程继续执行。
我有两个问题想请问下各位大佬:
- 上面的代码是产生图片中方法时间消耗的原因吗?
- 上面的代码会对性能产生影响吗,影响有多大?
- Python 有没有什么更好的方式来实现异步的通信转为同步呢?
谢谢大家!!!😋
Python中程序大量时间消耗在lock.acquire和time.sleep上,如何优化性能?
用 coroutinepython<br>async def hello():<br> print("Hello world!")<br> r = await asyncio.sleep(1)<br> print("Hello again!")<br>
锁和sleep卡住程序,多半是设计问题。核心思路是减少锁竞争和避免无意义等待。
1. 减少锁粒度 别用大锁,按数据拆分。比如全局字典,可以按key分片用多个锁:
import threading
from typing import Any
class ShardedDict:
def __init__(self, num_shards=16):
self.shards = [{} for _ in range(num_shards)]
self.locks = [threading.Lock() for _ in range(num_shards)]
def _get_shard(self, key):
return hash(key) % len(self.shards)
def get(self, key) -> Any:
shard_id = self._get_shard(key)
with self.locks[shard_id]:
return self.shards[shard_id].get(key)
def set(self, key, value):
shard_id = self._get_shard(key)
with self.locks[shard_id]:
self.shards[shard_id][key] = value
2. 用队列替代sleep轮询 别用sleep检查状态,用queue阻塞等待:
import queue
import threading
task_queue = queue.Queue()
def worker():
while True:
# 没任务时自动阻塞,不占CPU
task = task_queue.get()
process(task)
def producer():
task_queue.put(new_task) # 放任务自动唤醒worker
3. 换无锁数据结构 考虑用multiprocessing.Manager的Queue,或者直接上multiprocessing共享内存。
4. 检查锁持有时间 用cProfile看哪里持锁太久:
python -m cProfile -o profile_stats.py my_program.py
总结:拆锁、换队列、改架构。
啤酒饮料矿泉,水。。。。。。哦不是,asyncio 需要吗
这个图片是用什么工具生成的
pycharm 的功能吧
这种异步转同步有大量锁的等待很正常呀,Event 本来就是用 lock 实现的,对性能影响不大,要是真的那么在乎这一点性能,用 asyncio 或者 gevent
#1
#2
#3
#6
python2 不支持 asyncio 呀。。。
#4 PyCharm 在 run 的时候有一个 Profile 选项,用那个选项来运行就行了
那么 eventlet / gevent 了解一下…
等下我看反了… 你要异步变同步… = =!
#11 大佬能不能再详细的介绍介绍啊,萌新不太理解啊。。。
你要干啥 如果是 socket 收数据
收好的数据塞队列里
然后另外一个线程从队列里取就完了
不要没事就加锁
要性能的话可以自己实现协程
做个等待表
有结果回来的话就查表唤醒等到中的线程不就好了么?
再进一步,加上回调函数,有结果的话调用回调
我怎么觉得比你写这一套还要简单点?
#15 谢谢,我再琢磨琢磨,有不懂的再向您请教😋
eventlet 了解一下,另外,你是所有线程共享同一个 event ?
#17 是的,是所有的线程共享一个 event


