【问题标题】:Is it possible to check time being taken by a method call in parallel是否可以并行检查方法调用所花费的时间
【发布时间】:2020-04-28 03:24:41
【问题描述】:

在 Spring Boot 服务类中,假设我正在调用 processEvent() 的方法。
processEvent() 方法可能正在做 N 件事情,包括对其他服务进行 REST 调用。

如何并行检查该方法所花费的时间,如果它超过阈值,则执行其他操作,例如抛出异常?

class EventService {  

    public void processEvent(ServiceContext context, Event event) {     

      // Field a time checker here for the below method.  
      processEvent(event);  
    }  
    public void processEvent(Event event) {  
      // this method does many things.  
    }  
}

这可以使用CompletionService 来实现吗?如果有,请举个例子!

编辑:
以下代码有效,但我有一个查询:

public void processEvent(ServiceContext context, Event event) {  

LOGGER.debug("Timestamp before submitting task = {}", System.currentTimeMillis());  

  Future<EventResponse> future = executor.submit(() -> {  
    LOGGER.debug("Timestamp before invoking = {}", System.currentTimeMillis());
    EventResponse eventResponse = processEvent(event);  
    LOGGER.debug("Timestamp after invoking = {}", System.currentTimeMillis());  
    return eventResponse;  
  });  

  try {
    LOGGER.debug("Thread sleep starts at = {}", System.currentTimeMillis());
    Thread.sleep(5000);
    LOGGER.debug("Thread sleep ended at = {}", System.currentTimeMillis());
  } catch (InterruptedException e) {
    LOGGER.debug("Going to print stack trace....");
    e.printStackTrace();
  }  

  if (!future.isDone()) {
    future.cancel(true);
    LOGGER.debug("task executor cancelled at = {}", System.currentTimeMillis());
  } else {
    EventResponse response = future.get();
    LOGGER.debug("Received Event ID = {}", response.getEventDetailsList().get(0).getEventID());
    return response;
  }  

  LOGGER.debug("Going to return error response at = {}", System.currentTimeMillis());  
  throw new Exception("Message");  
}

我收到以下日志:

提交任务前的时间戳 = 1579005638324
线程睡眠开始于 = 1579005638326
调用前的时间戳 = 1579005638326
线程睡眠结束于 = 1579005638526
任务执行者在 = 1579005638527 取消
将在 = 1579005638527 处返回错误响应
调用后的时间戳 = 1579005645228

“task executor cancelled at”之后如何记录“Timestamp after invoking”?

【问题讨论】:

    标签: java multithreading spring-boot


    【解决方案1】:

    你可以使用ThreadPoolTaskExecutor提交任务,然后休眠一定时间,然后检查任务是否完成,如果还在工作就中断它。但是,您不能仅仅终止任务,您必须定期检查任务本身内部的中断标志。代码类似于:

    @Autowired
    private ThreadPoolTaskExecutor executor;
    
    // ...
    
    Future<?> future = executor.submit(() -> {
    
      doOneThing();
    
      if(Thread.interrupted()) {
        return;
      }
    
      doAnotherThing();
    
      if(Thread.interrupted()) {
        return;
      }
    
      // etc.
    });
    
    Thread.sleep(10000);
    
    if (!future.isDone()) {
      future.cancel(true);
    }
    

    【讨论】:

    • 我通过参考您的代码更新了我的问题。请回答!
    • @mukund 如果您在processEvent 方法中检查Thread.interrupted(),您的代码并不清楚。调用future.cancel(true) 不会立即终止您的任务——它只是将线程标记为中断。在你正在运行的任务中,你应该定期检查 interrupted 标志,如果设置了标志则返回。
    • 哦,谢谢。我认为future.cancel(true) 会终止任务。但是,当您说定期检查中断单位时,我如何确定位置以及应该检查多少次?我可以在return eventResponse; 之前检查一次吗?
    • 如果你在return前检查,这已经是所有工作完成后,所以它不会有任何影响。检查中断的方式和时间取决于代码的结构。如果有多个步骤,您可以在每个步骤之后检查。如果有循环,您可以检查每次迭代等。
    • 另外,如果繁重的方法正在调用 REST 服务,并且您想确保它不会无限期挂起,那么您可能只需为您的请求设置一些合理的超时,而不是从外部中断该方法。
    【解决方案2】:

    您可以将标准ThreadPoolExecutorScheduledThreadPoolExecutor 混合使用。如果前者还在运行,后者将取消前者的提交。

    ThreadPoolExecutor executor = ...;
    
    ScheduledThreadPoolExecutor watcher = ...;
    
    Future<?> future = executor.submit(() -> { ...  })
    
    watcher.schedule(() -> future.cancel(true), THRESHOLD_SECONDS, TimeUnit.SECONDS);
    
    

    future.cancel(true) 完成后将是空操作。尽管如此,您应该知道如何处理跨线程通信和取消。 cancel(true)“要么完全阻止它运行,要么如果它正在运行,则中断线程,指示我们需要立即完全停止执行”

    从那里您的Runnable 应该将中断作为停止条件处理:

    executor.submit(()-> {
       // do something
       if(Thread.currentThread().isInterrupted()) {
         // clean up and exit early
       }
       // continue doing something
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-30
      • 1970-01-01
      • 1970-01-01
      • 2020-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多