重用worker实例来处理多个任务

时间:2013-09-23 13:25:28

标签: java concurrency java.util.concurrent

我有一个Web测试应用程序(基于Selenium,但这在这里无关紧要),它连续执行了大量的测试用例。这需要几个小时才能完成,测试用例数量会增加,所以我想使用多个Web浏览器实例并行执行多个测试用例。测试用例彼此之间没有依赖关系。

非常简化它看起来像这样:

TestExecutor executor = new TestExecutor(new FirefoxDriver());
for (TestCase test: tests) {
    executor.execute(test);
    // use results here
}

现在我不确切知道如何并行化这个。我可以轻松地创建几个连接到多个Web浏览器的TestExecutor作为Callables并使用Executors,CompletitionServices和其他好帮手类,但我该怎么做:

  • 使用之前的TestCase准备好后,将新的TestCase传递给TestExecutor? Callable中的call()方法不带参数,所以我可能必须在我的TestExecutor类中实现一些setNextTestCase()来实现这一点,我发现它并不是很好。还有更好的选择吗?

  • 重用TestExecutor实例来执行下一个测试用例?由于每个TestExecutor实例都需要一个Web浏览器实例,因此如果我为每个测试用例创建一个新的TestExecutor,则需要很长时间来初始化它并导致许多窗口在屏幕上闪烁。

2 个答案:

答案 0 :(得分:5)

  

在使用之前的TestCase准备好后,将新的TestCase传递给TestExecutor?

如果我理解你,这是一个常见的问题。线程池中有许多线程,但每个线程都有一些上下文 - 在本例中是“Web浏览器”。您不希望为提交给线程池的每个作业启动新的浏览器。

以下是有关如何实现此目标的一些想法。

  • 拥有BlockingQueueTestCase个对象。然后,每个线程初始化其浏览器,然后从TestCase对象队列中出队,直到队列为空或某些shutdown boolean设置为true。您可以将TestExecutor个对象提交到线程池中,但是它们会通过您自己的队列自行将TestCase个对象出列。您TestCase提交给线程池。

    BlockingQueue<TestCase> testCaseQueue = new LinkedBlockingQueue<>();
    for (TestCase test: tests) {
        testCaseQueue.add(test);
    }
    // now make your executors and run them in a thread-pool
    TestExecutor testExecutor =
         new TestExecutor(new FirefoxDriver(), testCaseQueue);
    ExecutorService threadPool = Executors.newCachedThreadPool();
    threadPool.submit(testExecutor);
    ...
    // once you've submitted your last executor, you shutdown the pool
    threadPool.shutdown();
    
    ...
    // inside of the executor, they dequeue tests from `testCaseQueue`
    while (!shutdown) {
        TestCase testCase = testCaseQueue.poll(0, TimeUnit.MILLISECONDS);
        if (testCase == null) {
           break;
        }
        ...
    } 
    
  • 另一个想法是将TestCase提交给线程池并使用ThreadLocal来获取之前配置的测试浏览器。这不是最佳选择,因为在测试完成后很难适当地终止浏览器。

答案 1 :(得分:0)

我建议设置一个Queue / List TestExecutors。然后创建一个包裹Callable的{​​{1}} / Runnable。您TestCase的{​​{1}}与[{1}} ExecutorService的线程数相同。 List的第一步是从TestExecutors检索/弹出Runnable。它会利用执行程序进行测试,然后在完成时将其放回队列中。