Python 如何阻止进程空闲或被终止?
我需要处理数百万用户。我有数百万个用户ID,我从http请求中获取用户数据并写入文件 我使用多处理来执行这些任务的一批。然后,我在每个进程中使用多线程来成批执行任务。这大大提高了性能,使我能够以更快的速度处理更多的用户 问题: 我发现经过一段时间后,所有进程都变得不活动。我通过查看活动监视器了解这一点。一开始,我可以看到他们使用了大量的cpu和线程,过了一段时间,他们似乎空闲,我的程序挂起Python 如何阻止进程空闲或被终止?,python,python-3.x,python-multiprocessing,python-multithreading,Python,Python 3.x,Python Multiprocessing,Python Multithreading,我需要处理数百万用户。我有数百万个用户ID,我从http请求中获取用户数据并写入文件 我使用多处理来执行这些任务的一批。然后,我在每个进程中使用多线程来成批执行任务。这大大提高了性能,使我能够以更快的速度处理更多的用户 问题: 我发现经过一段时间后,所有进程都变得不活动。我通过查看活动监视器了解这一点。一开始,我可以看到他们使用了大量的cpu和线程,过了一段时间,他们似乎空闲,我的程序挂起 导入操作系统 导入时间 导入日志记录 导入多处理 导入配置 导入json 从google.cloud导入存
导入操作系统
导入时间
导入日志记录
导入多处理
导入配置
导入json
从google.cloud导入存储
从pymongo导入MongoClient,UpdateOne
从队列导入队列
导入线程
来自多处理导入池的cpu\u计数
进程=多处理。cpu\u计数()-1
def get_tweet_对象(用户、计数器、锁、进程):
#删除(调用http请求并将json文件写入磁盘
lock.acquire()
尝试:
counter.value=counter.value+1
最后:
lock.release()
打印(“应用程序ID:{APP_ID},剩余:{APP_剩余},总用户:{TOTAL_USERS},用户:{USER_ID},TWEETS数量:{NO_TWEETS},所用时间:{TIME_taked}”
.format(app_id=app.app_id,app_剩余=0,total_users=counter.value,user_id=user[“user_id]”),no_tweets=len(total_tweets),time_take=round((end-start),2)),threading.current_thread().name,proc)
def添加任务(任务队列,任务):
对于任务中的任务:
任务队列.放置(任务)
返回任务队列
def进程任务(任务队列、计数器、锁):
logger=多处理。get_logger()
proc=os.getpid()
而不是任务队列。空()
尝试:
user=task\u queue.get()
多线程(用户、计数器、锁、进程)
例外情况除外,如e:
记录器错误(e)
info(f'Process{proc}已成功完成')
返回真值
def管理队列(任务队列、计数器、锁、过程):
尽管如此:
user=task\u queue.get()
获取_tweet_对象(用户、计数器、锁、进程)
任务队列。任务完成()
def do_多线程(批处理、计数器、锁、过程):
“”“启动多线程”“”
#设置线程数。
线程数=5
#初始化队列。
任务队列=队列()
#启动多线程
对于范围内的i(线程数):
t=threading.Thread(目标=管理队列,args=[
任务(队列、计数器、锁、进程])
t、 daemon=True
t、 开始()
对于分批处理:
任务队列.放置(批处理)
任务队列。加入()
def run():
mongodb=MongoClient(host=config.MONGO_URI)[“twitter”]
现有用户=mongodb[屏幕名称].find({}).limit(10000)
批次=创建批次(共100个)(现有用户)
空任务队列=多处理.Manager().queue()
完整任务队列=添加任务(空任务队列,批次)
进程=[]
计数器=多处理。值('i',0)
lock=multiprocessing.lock()
打印(f'Running with{PROCESSES}PROCESSES!')
开始=时间。时间()
对于范围内的w(工艺):
p=多处理。进程(
目标=进程任务,参数=(完整任务队列,计数器,锁))
进程。追加(p)
p、 开始()
对于流程中的p:
p、 加入
打印(f'Time take={Time.Time()-start:.10f}')
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
多处理.log_to_stderr(logging.ERROR)
运行()
因此,代码存在多个问题。首先,要不惜一切代价避免无限循环,比如在manage\u queue
函数中。注意:我不是说“避免,而为True:
”,因为这并不意味着它是一个无限循环(例如,你可以在它里面有break
)
话虽如此,最大的问题(我们在聊天中的长时间讨论中发现)是get\u tweet\u object()
函数有时会出现异常而失败,当出现异常时task\u queue.task\u done()
永远不会被调用,因此task\u queue.join()
永远不会退出
另一个问题是,将而不是task\u queue.empty():
与task\u queue.get()
混合是一种竞争条件。当两个并行线程运行且task\u queue
正好有一个元素时会发生什么?其中一个元素将永远挂起。这应替换为task\u queue.get(False)
使用适当的队列。空的捕获。它看起来像化妆品,但事实是竞争条件在.get()
调用中处理。这样,您还需要在生成线程之前填充队列
总而言之,这里有一些变化:
from queue import Empty
def do_multithreading(batches, counter, lock, proc):
"""Starts the multithreading"""
# Set the number of threads.
number_of_threads = 5
# Initializes the queue.
for batch in batches:
task_queue.put(batch)
# Starts the multithreading
for i in range(number_of_threads):
t = threading.Thread(target=manage_queue, args=[
task_queue, counter, lock, proc])
t.daemon = True
t.start()
task_queue.join()
def manage_queue(task_queue, counter, lock, proc):
while True:
try:
user = task_queue.get(False)
except Empty:
break
try:
get_tweet_objects(user, counter, lock, proc)
except Exception as exc:
print(exc)
finally:
task_queue.task_done()
def process_tasks(task_queue, counter, lock):
logger = multiprocessing.get_logger()
proc = os.getpid()
while True:
try:
user = task_queue.get(False)
except Empty:
break
try:
do_multithreading(user, counter, lock, proc)
except Exception as e:
logger.error(e)
logger.info(f'Process {proc} completed successfully')
return True
话虽如此,我还是强烈建议使用。它们是空闲的,因为您有一个严重的漏洞:您的manage\u队列
从不退出(即使队列是空的)您为每个用户生成了5个。如果您有10000个用户,那么很可能您的文件描述符已经用完了。如果您在每个用户中都接触http和文件,则可能会更快。但是您确实会记录错误,您的处理任务
函数不会记录它们吗?@奇怪的是,我正在查找这些日志,它只是在get_tweet_objects functionOk,首先重构所有代码。使用而不是手动生成它们(你做错了)。然后永远不要允许像manage_queue
这样的无限任务。对于执行者,您无论如何都会被迫这样做。我确信您的问题是描述符用完了,而日志记录无法工作,因为…描述符用完了。XD您可能会登录到文件,对吗?然后取决于底层日志记录实现在上,您可能看不到任何结果。如果没有文件描述符,您就是瞎子。谢谢,我可能一直在遵循一个旧的多处理指南。(edi)