[英]Running time of a job sent to a ExecutorService
美好的一天,
我正在編寫一個程序,其中對從文本文件讀取的每一行調用一個方法。 由於此方法的每次調用均獨立於其他任何行讀取,因此我可以並行調用它們。 為了最大限度地利用cpu,我使用ExecutorService來提交每個run()調用。 由於文本文件有1500萬行,因此我需要錯開ExecutorService運行以一次不創建太多作業(OutOfMemory異常)。 我還想跟蹤每次提交的運行的運行時間,因為我發現有些運行尚未完成。 問題是,當我嘗試將Future.get方法與超時一起使用時,超時是指它進入ExecutorService隊列的時間,而不是指它甚至從開始運行就開始運行的時間。 我想花一些時間,因為它開始運行,而不是因為它進入了隊列。
代碼如下:
ExecutorService executorService= Executors.newFixedThreadPool(ncpu);
line = reader.readLine();
long start = System.currentTimeMillis();
HashMap<MyFut,String> runs = new HashMap<MyFut, String>();
HashMap<Future, MyFut> tasks = new HashMap<Future, MyFut>();
while ( (line = reader.readLine()) != null ) {
String s = line.split("\t")[1];
final String m = line.split("\t")[0];
MyFut f = new MyFut(s, m);
tasks.put(executorService.submit(f), f);
runs.put(f, line);
while (tasks.size()>ncpu*100){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Iterator<Future> i = tasks.keySet().iterator();
while(i.hasNext()){
Future task = i.next();
if (task.isDone()){
i.remove();
} else {
MyFut fut = tasks.get(task);
if (fut.elapsed()>10000){
System.out.println(line);
task.cancel(true);
i.remove();
}
}
}
}
}
private static class MyFut implements Runnable{
private long start;
String copy;
String id2;
public MyFut(String m, String id){
super();
copy=m;
id2 = id;
}
public long elapsed(){
return System.currentTimeMillis()-start;
}
@Override
public void run() {
start = System.currentTimeMillis();
do something...
}
}
如您所見,我嘗試跟蹤已發送的作業數,如果超過了閾值,我會稍等片刻,直到一些作業完成。 我還要嘗試檢查是否有任何作業花費太長時間才能取消它,請牢記哪個失敗,然后繼續執行。 這不是我希望的那樣。 一項任務執行10秒的時間遠遠超出了需要的時間(根據機器和CPU的數量,我會在70到130秒內完成1000行代碼)。
我究竟做錯了什么? 我的Runnable類中的run方法是否不應該僅在ExecutorService中的某些線程空閑並開始對其工作時才調用? 我得到許多結果,這些結果花費了超過10秒的時間。 有沒有更好的方法來實現我的目標?
謝謝。
如果使用的是Future,我建議將Runnable更改為Callable並返回執行線程的總時間作為結果。 下面是示例代碼:
import java.util.concurrent.Callable;
public class MyFut implements Callable<Long> {
String copy;
String id2;
public MyFut(String m, String id) {
super();
copy = m;
id2 = id;
}
@Override
public Long call() throws Exception {
long start = System.currentTimeMillis();
//do something...
long end = System.currentTimeMillis();
return (end - start);
}
}
您正在使工作更加努力。 Java的框架提供了您想要的一切,您只需要使用它即可。
限制待審批工作項的數目使用界隊列的作品,但ExecutorService
由歸國Executors.newFixedThreadPool()
使用未綁定的隊列。 一旦有界隊列已滿,要等待的策略可以通過RejectedExecutionHandler
來實現。 整個過程看起來像這樣:
static class WaitingRejectionHandler implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
executor.getQueue().put(r);// block until capacity available
} catch(InterruptedException ex) {
throw new RejectedExecutionException(ex);
}
}
}
public static void main(String[] args)
{
final int nCPU=Runtime.getRuntime().availableProcessors();
final int maxPendingJobs=100;
ExecutorService executorService=new ThreadPoolExecutor(nCPU, nCPU, 1, TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(maxPendingJobs), new WaitingRejectionHandler());
// start flooding the `executorService` with jobs here
就這樣。
測量作業中所經過的時間是很容易的,因為它沒有任何關系與多線程:
long startTime=System.nanoTime();
// do your work here
long elpasedTimeSoFar = System.nanoTime()-startTime;
但是,一旦使用有限隊列,也許您就不再需要它了。
順便說Future.get
帶有超時的Future.get
方法不引用自從它進入ExecutorService隊列以來的時間,而是引用調用get
方法本身的時間。 換句話說,它告訴get
方法允許等待多長時間,僅此而已。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.