为了账号安全,请及时绑定邮箱和手机立即绑定

有限制的运行配置

有限制的运行配置

胡说叔叔 2022-11-30 10:55:10
我对如何继续我的代码有疑问。我的项目是一个在后台一个一个地运行配置的工具。我想为运行配置的数量添加一个限制。例如,如果我有 13 个配置,我想每次运行 5 个配置,所以顺序是:- Running 5 configurations- All 5 configurations done running- Running 5 configurations- All 5 configurations done running- Running 3 configurations- All 3 configurations done running现在的代码,工作如下:public void runConfigurations(List<ConfigStruct> configurations) {    for (ConfigStruct configuration : configurations) {        try {            configuration.run();        } catch (ConfigurationException e) {            continue;        }    }}现在,它一个一个地运行每个配置。该run方法如下所示:public void run() throws ConfigurationException {    StringBuffer runCmd = generateGalishFullCommand(GalishFlags.RUN);    try {        ExternalCommandExecutor.execute(runCmd, "Failed to run " + name, true, true);    }  catch (IOException e) {        throw new ConfigurationException(e.getMessage());    }}外观签名execute如下:public static String execute(final String cmd, final String error, final boolean runInBackground, final boolean retry) throws IOException;起初,我虽然可以不在后台每 5 个配置运行最后一个配置,但它有问题。我不能不在后台执行每 5 个配置的最后一个配置,因为第一个配置可能最后完成。我该如何解决这个问题?编辑:当我打印配置时,它看起来如下:[com.configStructs@3f15dbec, com.configStructs@31d2327e]此外,这configurations是一个列表configStructs。
查看完整描述

2 回答

?
神不在的星期二

TA贡献1963条经验 获得超6个赞

抱歉vesii如果我不完全理解甚至误解你的问题,但你的英语不是很好,即使在你发表评论后我也有问题,看看使用多线程的问题是什么。


无论如何,我建议你让你的ConfigStruct类实现Runnable接口,这很容易,因为它已经有一个run()方法。您只需要摆脱抛出已检查的异常,因此我进一步建议创建ConfigurationException一个RuntimeException您不必在方法签名中声明的异常。


不幸的是,您没有提供完整的MCVE,只有代码片段。所以我必须弥补其余部分才能编译和运行您的代码。我只是添加了一些简单的助手/虚拟类。我的解决方案如下所示:


package de.scrum_master.app;


public enum GalishFlags {

    RUN

}

package de.scrum_master.app;


public class ConfigurationException extends RuntimeException {

  private static final long serialVersionUID = 1L;


  public ConfigurationException(String message, Throwable cause) {

    super(message, cause);

  }

}

package de.scrum_master.app;


import java.io.IOException;


public class ExternalCommandExecutor {

  public static String execute(final String cmd, final String error, final boolean runInBackground, final boolean retry) throws IOException {

    System.out.println("Executing external command: " + cmd);

    try {

      Thread.sleep(100);

    } catch (InterruptedException e) {

      e.printStackTrace();

    }

    return cmd;

  }

}

如您所见,命令执行器在将内容打印到控制台后等待 100 毫秒。如果您希望程序运行得更慢或什至随机化以模拟需要不同时间完成的命令,您也可以将其更改为 1000 毫秒。


现在我们需要一个小的驱动程序应用程序,我们可以在其中生成配置并运行它们。解决你从不同时运行超过 5 个线程的问题的关键是通过创建一个固定的线程池Executors.newFixedThreadPool(5)。其余的应该很容易理解。


package de.scrum_master.app;


import java.io.IOException;


public class ConfigStruct implements Runnable {

  private String name;


  public ConfigStruct(String name) {

    this.name = name;

  }


  @Override

  public void run() {

    StringBuffer runCmd = generateGalishFullCommand(GalishFlags.RUN);

    try {

      ExternalCommandExecutor.execute(runCmd.toString(), "Failed to run " + name, true, true);

    } catch (IOException e) {

      throw new ConfigurationException(e.getMessage(), e);

    }

  }


  private StringBuffer generateGalishFullCommand(GalishFlags run) {

    return new StringBuffer("Galish full command for ConfigStruct '" + name + "'");

  }

}

package de.scrum_master.app;


import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;


public class Application {

  public void runConfigurations(List<ConfigStruct> configurations) {

    for (ConfigStruct configuration : configurations) {

      try {

        configuration.run();

      } catch (ConfigurationException e) {

        continue;

      }

    }

  }


  public void runConfigurationsThreaded(List<ConfigStruct> configurations) {

    ExecutorService executorService = Executors.newFixedThreadPool(5);

    for (ConfigStruct configuration : configurations)

      executorService.execute(configuration);

    executorService.shutdown();

    try {

      executorService.awaitTermination(30, TimeUnit.SECONDS);

    } catch (InterruptedException e) {

      e.printStackTrace();

    }

  }


  public static void main(String[] args) {

    List<ConfigStruct> configurations = new ArrayList<>();

    for (int i = 1; i <= 13; i++)

      configurations.add(new ConfigStruct("Configuration " + i));


    long startTime = System.currentTimeMillis();

    new Application().runConfigurations(configurations);

    System.out.println("Total time (1 thread)  = " + (System.currentTimeMillis() - startTime) + " ms");

    System.out.println();


    startTime = System.currentTimeMillis();

    new Application().runConfigurationsThreaded(configurations);

    System.out.println("Total time (5 threads) = " + (System.currentTimeMillis() - startTime) + " ms");

  }

}

控制台日志将如下所示:


Executing external command: Galish full command for ConfigStruct 'Configuration 1'

Executing external command: Galish full command for ConfigStruct 'Configuration 2'

Executing external command: Galish full command for ConfigStruct 'Configuration 3'

Executing external command: Galish full command for ConfigStruct 'Configuration 4'

Executing external command: Galish full command for ConfigStruct 'Configuration 5'

Executing external command: Galish full command for ConfigStruct 'Configuration 6'

Executing external command: Galish full command for ConfigStruct 'Configuration 7'

Executing external command: Galish full command for ConfigStruct 'Configuration 8'

Executing external command: Galish full command for ConfigStruct 'Configuration 9'

Executing external command: Galish full command for ConfigStruct 'Configuration 10'

Executing external command: Galish full command for ConfigStruct 'Configuration 11'

Executing external command: Galish full command for ConfigStruct 'Configuration 12'

Executing external command: Galish full command for ConfigStruct 'Configuration 13'

Total time (1 thread)  = 1374 ms


Executing external command: Galish full command for ConfigStruct 'Configuration 1'

Executing external command: Galish full command for ConfigStruct 'Configuration 2'

Executing external command: Galish full command for ConfigStruct 'Configuration 3'

Executing external command: Galish full command for ConfigStruct 'Configuration 4'

Executing external command: Galish full command for ConfigStruct 'Configuration 5'

Executing external command: Galish full command for ConfigStruct 'Configuration 6'

Executing external command: Galish full command for ConfigStruct 'Configuration 7'

Executing external command: Galish full command for ConfigStruct 'Configuration 8'

Executing external command: Galish full command for ConfigStruct 'Configuration 10'

Executing external command: Galish full command for ConfigStruct 'Configuration 9'

Executing external command: Galish full command for ConfigStruct 'Configuration 11'

Executing external command: Galish full command for ConfigStruct 'Configuration 13'

Executing external command: Galish full command for ConfigStruct 'Configuration 12'

Total time (5 threads) = 344 ms

请注意:

  • 在单线程循环中运行时,运行时间 > 1,300 毫秒 (13 x 100 毫秒)。

  • 在具有 5 个线程的线程池中运行时,运行时间 > 300 毫秒(3 x 100 毫秒)- 根据同时处理 5 个配置的要求,这正是您所期望的。

  • 由于多线程,日志输出不是直接从 1 到 13,而是有点不同,这里最后是 8、10、9、11、13、12。对于每个线程的不同处理时间,它看起来会更加不同。


更新:如果你想看到更多变化,只需在线程的睡眠时间中添加一个随机元素并稍微扩展日志记录:

package de.scrum_master.app;


import java.io.IOException;

import java.util.Random;


public class ExternalCommandExecutor {

  private static final Random RANDOM = new Random();


  public static String execute(final String cmd, final String error, final boolean runInBackground, final boolean retry) throws IOException {

    long sleepTime = 100 + 100 * (RANDOM.nextInt(3));

    System.out.println("Executing external command: " + cmd + ", sleeping for " + sleepTime + " ms");

    try {

      Thread.sleep(sleepTime);

    } catch (InterruptedException e) {

      e.printStackTrace();

    }

    System.out.println("Finished execution: " + cmd);

    return cmd;

  }

}

然后控制台日志可能如下所示:


Executing external command: Galish full command for ConfigStruct 'Configuration 1', sleeping for 300 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 1'

Executing external command: Galish full command for ConfigStruct 'Configuration 2', sleeping for 100 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 2'

Executing external command: Galish full command for ConfigStruct 'Configuration 3', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 3'

Executing external command: Galish full command for ConfigStruct 'Configuration 4', sleeping for 300 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 4'

Executing external command: Galish full command for ConfigStruct 'Configuration 5', sleeping for 100 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 5'

Executing external command: Galish full command for ConfigStruct 'Configuration 6', sleeping for 100 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 6'

Executing external command: Galish full command for ConfigStruct 'Configuration 7', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 7'

Executing external command: Galish full command for ConfigStruct 'Configuration 8', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 8'

Executing external command: Galish full command for ConfigStruct 'Configuration 9', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 9'

Executing external command: Galish full command for ConfigStruct 'Configuration 10', sleeping for 100 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 10'

Executing external command: Galish full command for ConfigStruct 'Configuration 11', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 11'

Executing external command: Galish full command for ConfigStruct 'Configuration 12', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 12'

Executing external command: Galish full command for ConfigStruct 'Configuration 13', sleeping for 100 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 13'

Total time (1 thread)  = 2314 ms


Executing external command: Galish full command for ConfigStruct 'Configuration 1', sleeping for 300 ms

Executing external command: Galish full command for ConfigStruct 'Configuration 2', sleeping for 300 ms

Executing external command: Galish full command for ConfigStruct 'Configuration 3', sleeping for 200 ms

Executing external command: Galish full command for ConfigStruct 'Configuration 5', sleeping for 300 ms

Executing external command: Galish full command for ConfigStruct 'Configuration 4', sleeping for 100 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 4'

Executing external command: Galish full command for ConfigStruct 'Configuration 6', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 3'

Executing external command: Galish full command for ConfigStruct 'Configuration 7', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 1'

Finished execution: Galish full command for ConfigStruct 'Configuration 2'

Executing external command: Galish full command for ConfigStruct 'Configuration 8', sleeping for 200 ms

Executing external command: Galish full command for ConfigStruct 'Configuration 9', sleeping for 100 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 5'

Executing external command: Galish full command for ConfigStruct 'Configuration 10', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 6'

Executing external command: Galish full command for ConfigStruct 'Configuration 11', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 9'

Finished execution: Galish full command for ConfigStruct 'Configuration 7'

Executing external command: Galish full command for ConfigStruct 'Configuration 12', sleeping for 200 ms

Executing external command: Galish full command for ConfigStruct 'Configuration 13', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 8'

Finished execution: Galish full command for ConfigStruct 'Configuration 10'

Finished execution: Galish full command for ConfigStruct 'Configuration 11'

Finished execution: Galish full command for ConfigStruct 'Configuration 13'

Finished execution: Galish full command for ConfigStruct 'Configuration 12'

Total time (5 threads) = 609 ms

看看在单线程模式下如何仍然一切都是 FIFO(先进先出)?


另请注意,如果您计算控制台上活动(未完成)线程的数量,无论执行时间如何,它都不会超过 5。最后 5 个线程结束。而且总执行时间仍然明显小于单线程情况。


更新 2:最后但同样重要的是,如果将主循环中的元素数量从 13 增加到更大的数字,比如 100,您会注意到最终多线程解决方案的总执行时间大约为 1/单线程解决方案的5(或者一般是1除以固定线程池的线程数)。这是因为线程除了等待并打印到控制台外没有做太多其他事情。如果他们实际上做了更多的事情,例如繁重的计算或大量的 I/O,那么改进将不那么显着,但仍然很重要。


我对 100 个配置元素的尝试产生了以下输出(缩写):


Executing external command: Galish full command for ConfigStruct 'Configuration 1', sleeping for 300 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 1'

(...)

Executing external command: Galish full command for ConfigStruct 'Configuration 100', sleeping for 300 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 100'

Total time (1 thread)  = 20355 ms


Executing external command: Galish full command for ConfigStruct 'Configuration 2', sleeping for 100 ms

Executing external command: Galish full command for ConfigStruct 'Configuration 1', sleeping for 300 ms

(...)

Executing external command: Galish full command for ConfigStruct 'Configuration 100', sleeping for 200 ms

Finished execution: Galish full command for ConfigStruct 'Configuration 99'

Finished execution: Galish full command for ConfigStruct 'Configuration 93'

Finished execution: Galish full command for ConfigStruct 'Configuration 94'

Finished execution: Galish full command for ConfigStruct 'Configuration 95'

Finished execution: Galish full command for ConfigStruct 'Configuration 100'

Total time (5 threads) = 3923 ms

看?~20 秒 / 5 = ~4 秒


查看完整回答
反对 回复 2022-11-30
?
郎朗坤

TA贡献1921条经验 获得超9个赞

在我看来你正在寻找一个java.util.concurrent.CountDownLatch. 你给它一个数字(在你的情况下是 5)并等待一段时间让它达到 0。谷歌会给你很多如何使用它的例子。



查看完整回答
反对 回复 2022-11-30
  • 2 回答
  • 0 关注
  • 75 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信