You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现多频次bash脚本在一分钟内的均匀间隔调度

如何实现多频次bash脚本在一分钟内的均匀间隔调度

我现在有一个tasks.yaml配置文件,里面定义了多个bash脚本,每个脚本需要在一分钟内运行指定的次数:

every_minute:
    - name: test0
      path: test0.sh
      times_per_min: 3
    - name: test1
      path: test1.sh
      times_per_min: 2

原本我用Python写了一段调度代码,思路是把每个脚本按重复次数塞进任务列表,然后按固定间隔依次执行:

import subprocess
import time
import yaml

TASKS = []
PROCESSES = []  # 补充原代码遗漏的变量定义

with open("tasks.yaml") as file:
  measurements = yaml.safe_load(file)

for every_min in measurements["every_minute"]:
  for _ in range(every_min["times_per_min"]):
    TASKS.append(every_min["path"])

interval = 60 / len(TASKS)

for task in TASKS:
  try:
    PROCESSES.append(subprocess.Popen([task]))
  except Exception as e:
    print(f"An error occurred: {e}")  # 优化错误提示,展示具体异常
  time.sleep(interval)

但这样生成的任务列表是连续重复同一个脚本:

print(TASKS)
# 输出: ['test0.sh', 'test0.sh', 'test0.sh', 'test1.sh', 'test1.sh']

我想要的是均匀穿插调度的效果,让不同脚本尽量交替执行,最终任务列表应该是:

['test0.sh', 'test1.sh', 'test0.sh', 'test1.sh', 'test0.sh']

而且这个逻辑要能适配任意数量的任务、任意的times_per_min值,不能只针对当前示例。


解决方案:按“轮询优先级”生成均匀穿插的任务列表

核心思路是:不要先把同一个脚本的所有实例都塞进列表,而是每次从所有任务中挑选还没达到执行次数的脚本,按轮询的方式依次添加,直到所有任务的执行次数都满足要求。

具体实现代码如下:

import subprocess
import time
import yaml

PROCESSES = []

with open("tasks.yaml") as file:
  measurements = yaml.safe_load(file)

# 先整理任务元数据:保存每个脚本的路径和剩余需要执行的次数
task_queue = [
    {"path": item["path"], "remaining": item["times_per_min"]}
    for item in measurements["every_minute"]
]

TASKS = []

# 循环生成均匀穿插的任务列表
while task_queue:
    # 遍历当前还有剩余执行次数的任务(用list(range)避免遍历时队列长度变化的问题)
    for idx in list(range(len(task_queue))):
        task = task_queue[idx]
        TASKS.append(task["path"])
        task["remaining"] -= 1
        # 若该任务已完成所有执行次数,从队列中移除
        if task["remaining"] == 0:
            del task_queue[idx]

# 计算执行间隔,避免除以0的情况
total_tasks = len(TASKS)
interval = 60 / total_tasks if total_tasks > 0 else 0

# 执行任务
for task_idx, task in enumerate(TASKS):
    try:
        PROCESSES.append(subprocess.Popen([task]))
    except Exception as e:
        print(f"执行任务 {task} 时出错: {e}")
    # 最后一个任务不需要sleep,避免额外等待间隔
    if task_idx != total_tasks - 1 and total_tasks > 0:
        time.sleep(interval)

逻辑验证

针对示例中的配置,生成的TASKS列表正好符合预期:

print(TASKS)
# 输出: ['test0.sh', 'test1.sh', 'test0.sh', 'test1.sh', 'test0.sh']

如果测试更复杂的场景,比如:

every_minute:
    - name: test0
      path: test0.sh
      times_per_min: 4
    - name: test1
      path: test1.sh
      times_per_min: 3
    - name: test2
      path: test2.sh
      times_per_min: 2

生成的任务列表会是:

['test0.sh', 'test1.sh', 'test2.sh', 'test0.sh', 'test1.sh', 'test2.sh', 'test0.sh', 'test1.sh', 'test0.sh']

完全符合均匀穿插的预期。


代码解释

  1. 任务队列初始化:用字典保存每个任务的路径和剩余执行次数,方便跟踪执行进度。
  2. 轮询生成列表:循环遍历剩余任务,每添加一个任务就减少其剩余次数,完成次数的任务会被从队列中移除,直到所有任务都达标。
  3. 执行逻辑优化:最后一个任务执行后不再sleep,避免多等待一个间隔;同时优化了错误提示,能看到具体的异常信息。

这样的实现完全通用,不管有多少个任务、每个任务需要执行多少次,都能保证任务被尽可能均匀地穿插调度,满足一分钟内的分布要求。

备注:内容来源于stack exchange,提问作者Jesper.Lindberg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 11:10:27