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 之后唤醒请求线程,之后请求线程继续执行。

我有两个问题想请问下各位大佬:

  1. 上面的代码是产生图片中方法时间消耗的原因吗?
  2. 上面的代码会对性能产生影响吗,影响有多大?
  3. Python 有没有什么更好的方式来实现异步的通信转为同步呢?

谢谢大家!!!😋


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

19 回复

用 coroutine
python<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 了解一下

啤酒饮料矿泉,水。。。。。。哦不是,asyncio 需要吗

这个图片是用什么工具生成的

pycharm 的功能吧

这种异步转同步有大量锁的等待很正常呀,Event 本来就是用 lock 实现的,对性能影响不大,要是真的那么在乎这一点性能,用 asyncio 或者 gevent

#1
#2
#3
#6
python2 不支持 asyncio 呀。。。

#4 PyCharm 在 run 的时候有一个 Profile 选项,用那个选项来运行就行了

那么 eventlet / gevent 了解一下…

等下我看反了… 你要异步变同步… = =!

用协程

#11 大佬能不能再详细的介绍介绍啊,萌新不太理解啊。。。

你要干啥 如果是 socket 收数据
收好的数据塞队列里
然后另外一个线程从队列里取就完了

不要没事就加锁

#13 但是请求是同步的,我得在前端知道数据是否已经获取到了啊。。。

要性能的话可以自己实现协程
做个等待表
有结果回来的话就查表唤醒等到中的线程不就好了么?
再进一步,加上回调函数,有结果的话调用回调

我怎么觉得比你写这一套还要简单点?

#15 谢谢,我再琢磨琢磨,有不懂的再向您请教😋

eventlet 了解一下,另外,你是所有线程共享同一个 event ?

#17 是的,是所有的线程共享一个 event

回到顶部