如何在Python3.8.10中使用多处理而不使用time.sleep()时休眠进程

如何在Python3.8.10中使用多处理而不使用time.sleep()时休眠进程,python,multiprocessing,Python,Multiprocessing,我使用大约1500 process multiprocessing.pool并行运行任务 在每个进程中,我需要每5分钟执行一次函数,即每5分钟我需要同时读取1500个文件。对于每个线程,我使用time.sleep(300-start_time)在函数执行结束时执行。然而,当尝试睡眠时,由于我的电脑中有16个内核,所以只执行16个进程,其余所有进程都不工作 代码如下: import logging import os import pandas as pd import datetime as d

我使用大约1500 process multiprocessing.pool并行运行任务

在每个进程中,我需要每5分钟执行一次函数,即每5分钟我需要同时读取1500个文件。对于每个线程,我使用time.sleep(300-start_time)在函数执行结束时执行。然而,当尝试睡眠时,由于我的电脑中有16个内核,所以只执行16个进程,其余所有进程都不工作

代码如下:

import logging
import os
import pandas as pd
import datetime as dt
os.chdir(os.path.dirname(os.path.abspath(__file__)))
from scipy.stats import linregress
from multiprocessing import Process, set_start_method, Pool, Value
import multiprocessing as mp
import traceback
import atr_bb as ab
import numpy as np
from operator import getitem
import datetime as dt
import time
import api as sa
import requests

def init(args):
    ''' store the counter for later use '''
    global counter
    counter = args


def train(string, dones, left_after):
    global counter

    count=0
    t0 = dt.datetime.now()
    time.sleep(300.0 - (t0.minute%5*60 + t0.second))
    
    while(True):
        count+=1
        t0 = dt.datetime.now()
        if(t0.hour>20):
            return
        
        print(string, " ", count)
        func(string)# function to be performed
        time.sleep(300.0 - (t0.minute%5*60 + t0.second) + 10)

        except:
            traceback.print_exc()


if __name__ == '__main__':
    l = pd.read_csv('data.csv')['A']
    counter = Value('i', 0)
    mp.freeze_support()
    mp_manager = mp.Manager()
    data_dict = mp_manager.dict()
    dones = mp_manager.list()
    left = mp_manager.list()
    args = []
    for string in l:
        args.append((str(string), dones, left))
    
    p = Pool(initializer = init, initargs = (counter, ))
    p.starmap(train, args)
    p.close()
    p.join()
现在,我怎样才能暂停一个进程一段时间,而CPU可以被其他进程利用呢?似乎是时间。sleep()仍在使用CPU,而其他字符串并没有被完全处理

根据BooBoo发布的答案2,我已经尝试了下面的代码,请您建议是否可以加快速度

import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
import logging
import pandas as pd
from scipy.stats import linregress
from multiprocessing import Process, set_start_method, Pool, Value
import multiprocessing as mp
import traceback
import atr_bb as ab
import numpy as np
from operator import getitem
import datetime as dt
import time
import stock_api as sa
import requests
import asyncio
import time
import signal
from multiprocessing import Manager
import sys

SLEEP_TIME = 5 * 60 # 5 minutes

async def train(x, lock):
    loop = asyncio.get_running_loop()
    """
    # I/O-bound portion:
    import aiofiles
    async with aiofiles.open(x, mode='r') as f:
        data = await f.read()
    # CPU-bound portion:
    loop.run_in_executor(None, train_part2, data)
    """
    # but no I/O-bound portion for this demo:

    async with lock:
        data = get_data(x)
        
    loop.run_in_executor(None, train_part2, data, x)
    

def train_part2(data, x):
    """ CPU-intensive portion """
    print(x, " ", data.iloc[-1,-1]) # just print data

async def dispatcher(x, lock):
    while(True):
        t0 = dt.datetime.now()
        if(t0.hour>=15):
            return
        
        t1 = time.time()
        await train(x, lock)
        t2 = time.time()
        time_to_sleep = SLEEP_TIME - (t2 - t1)
        if time_to_sleep > 0:
            await asyncio.sleep(time_to_sleep)

async def main():
    l = pd.read_csv('data.csv')['A']
    lock = asyncio.Lock()
    t0 = dt.datetime.now()
    
    coroutines = ((dispatcher(x, lock)) for x in l)
    await asyncio.gather(*coroutines)

    t1 = dt.datetime.now()
    
    print(t1-t0)
    
# Required for Windows:
if __name__ == "__main__":
    with Manager() as manager:
        asyncio.run(main())

我不认为池或你这样做的方式是最好的办法。如果您有一个池,它将有N个可以并行运行的工作线程。默认情况下,N是核心数。在你的情况下,工人做的第一件事就是睡觉。它阻止了池中的工人,但它什么也没做。您可以尝试增加池工作人员的数量,但如果您尝试启动1500名工作人员,则很可能会达到操作系统的限制

我的强烈建议是重新设计你的应用程序,这样你就可以在其他地方睡觉和等待,并且只在午睡时间结束后才将任务分派给泳池工作人员。不确定它是否适合您的情况,但您可以将线程和池结合起来。在线程中进行打盹,只有在有工作要做时才分派工作人员,并从池工作人员中移除所有睡眠

这只是一个虚拟片段来演示这个想法,但它可以运行

from multiprocessing import Pool
import threading
from time import sleep


def worker(x):
    print(x)

def dispatcher(p, y):
    p.apply(worker, args=(y,))

if __name__ == "__main__":
    p = Pool()
    for a in range(0,1500):
        threading.Timer(5, dispatcher, [p,a]).start()
    sleep(10)
    p.close()
    p.join()
在这个脚本中,
worker()
是您的
train()
。我的工人除了吐出序列号来证明它有效外,什么也不做。我添加了一个将在定时线程中运行的调度程序。这将完成等待,等待时间一结束,它就向池提交一个任务

如果您的
train()
需要很长时间才能完成,则此操作仍会阻塞。你仍然只有有限数量的游泳池工人,如果他们被实际工作所阻碍,那么你就不走运了。您可以尝试将池大小增加一点,但不能增加到数千。这不会阻塞,因为您只需等待五分钟,就像线程等待您一样


如果您只想不断重复相同的任务,还可以使用dispatcher函数启动另一个计时器线程作为其最终任务。

我知道您已经接受了答案。尽管如此,我还是建议使用
asyncio
corroutines的另一种方法,在创建如此大量(~15000)的并发任务时,这可能比线程更合适。此外,我想明确说明如何进行计时,以便重复操作(在您的情况下读取和处理文件,在我的示例中打印值)将在每5分钟执行一次的循环中完成

文件的实际读取是一个I/O绑定的“进程”,应该使用
PyPI
存储库中的
aiofiles
包来完成,正如我在评论中所指出的那样。然后,对已读取内容的CPU绑定处理应在进程池中完成,该功能由
asyncio
提供:

导入异步IO
导入时间
输入信号
从多处理导入管理器
导入系统
睡眠时间=5*60#5分钟
异步def序列(x):
loop=asyncio.get\u running\u loop()
"""
#I/O绑定部分:
导入文件
将aiofiles.open(x,mode='r')作为f进行异步:
数据=等待f.读取()
#CPU绑定部分:
循环运行执行器(无,第2列,数据)
"""
#但此演示没有I/O绑定部分:
循环运行执行器(无,第2列,x部分)
def系列第2部分(数据):
“”“CPU密集型部分”“”
打印(数据)#只打印数据
异步def调度程序(x):
而不是终止_flag.value:
t1=时间。时间()
等待列车(x)
t2=时间。时间()
睡眠时间=睡眠时间-(t2-t1)
如果睡眠时间>0:
等待异步睡眠(时间到睡眠)
异步def main():
协同路由=(范围(15000)内x的调度程序(x))
等待asyncio.gather(*协同路由)
def句柄控制(信号、堆栈帧):
终止标志值=1
#Windows需要:
如果名称=“\uuuuu main\uuuuuuuu”:
使用Manager()作为管理器:
终止\u标志=manager.Value('i',0)
signal.signal(signal.SIGINT,handle_ctrl_c)#终止于ctrl-c
asyncio.run(main())
更新

下面是我如何将使用协同路由与访问Web服务器结合起来,并将抓取限制在每秒一定的速率:

导入异步IO
进口aiohttp
导入时间
输入信号
从多处理导入管理器
导入系统
#每秒10个请求(但对于15000个请求,这将需要25分钟)
每秒请求数=10
睡眠时间=5*60#5分钟
上一秒发出的请求=1
首次请求的时间=无
异步def序列(会话,x):
第一个请求的全局时间,最后一秒发出的请求
loop=asyncio.get\u running\u loop()
#确保每秒执行的请求不超过相应的请求数:
如果首次请求的时间为无:
首次请求的时间=time.time()
其他:
t=time.time()
如果t-首次请求的时间>=1:
#新的第二个间隔的开始
首次请求的时间=t
上一秒发出的请求=1
elif上一秒发出的请求<每秒请求数:
上一秒发出的请求数+=1
其他:
#必须等待新的秒数:
#这将阻止所有协同路由:
睡眠时间(首次请求的时间+1-t)
"""
#I/O绑定部分:
与se异步