// // ThreadUtil.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad.util; import visad.VisADException; import java.util.ArrayList; import java.rmi.RemoteException; import java.util.Hashtable; import java.util.Enumeration; import java.util.List; /** * This class provides support for running a collection of Runnables * concurrently. It will collect and then throw any exceptions that * are thrown. It uses the static maxThreads as the number of threads * to run. The default is 1, resulting in sequential execution. */ public class ThreadManager { /** */ private String name = "ThreadManager"; /** */ private static int maxThreads = 1; /** */ private List<VisADException> visadExceptions = new ArrayList<VisADException>(); /** */ private List<RemoteException> remoteExceptions = new ArrayList<RemoteException>(); /** */ private List<RuntimeException> runtimeExceptions = new ArrayList<RuntimeException>(); /** */ private int numThreadsRunning = 0; /** */ private Object MUTEX = new Object(); /** */ private boolean running = false; /** */ private List<MyRunnable> runnables = new ArrayList<MyRunnable>(); /** */ private int myMaxThreads; /** */ private static Hashtable<Integer, Integer[]> times = new Hashtable<Integer, Integer[]>(); public boolean debug = false; /** * */ public ThreadManager() { this("ThreadManager"); } /** * Constructor with name specified * * @param theName */ public ThreadManager(String theName) { this.name = theName; if (maxThreads <= 0) { maxThreads = Runtime.getRuntime().availableProcessors(); } myMaxThreads = maxThreads; } /** * * * @param maxThreads */ public ThreadManager(int maxThreads) { myMaxThreads = maxThreads; } /** * * * @param max */ public static void setGlobalMaxThreads(int max) { ThreadManager.maxThreads = max; } public void debug(String msg) { } /** * */ public static void clearTimes() { times = new Hashtable<Integer, Integer[]>(); } /** * * * @param runnable */ public void addRunnable(MyRunnable runnable) { runnables.add(runnable); } /** * * * @param exc */ public void handleException(Exception exc) { if (exc instanceof VisADException) { visadExceptions.add((VisADException)exc); } else if (exc instanceof RemoteException) { remoteExceptions.add((RemoteException)exc); } else if (exc instanceof RuntimeException) { runtimeExceptions.add((RuntimeException)exc); } else { runtimeExceptions.add(new RuntimeException(exc)); } } /** * */ public void runnableStopped() { synchronized (MUTEX) { numThreadsRunning--; } } /** * */ public void runnableStarted() { synchronized (MUTEX) { numThreadsRunning++; } } /** * * * @return count of Runnables */ public int getNumRunnables() { return runnables.size(); } /** * * * @param maxThreads * * @throws RemoteException * @throws VisADException */ public void runInParallel(int maxThreads) throws VisADException, RemoteException { myMaxThreads = maxThreads; runInParallel(); } /** * * * @throws RemoteException * @throws VisADException */ public void runSequentially() throws VisADException, RemoteException { myMaxThreads = 1; runInParallel(); } /** * * * @throws RemoteException * @throws VisADException */ public void runAllParallel() throws VisADException, RemoteException { runInParallel(runnables.size()); } /** * * * @throws RemoteException * @throws VisADException */ public void runInParallel() throws VisADException, RemoteException { runInParallel(true); } /** */ public static final int MAX_THREADS = 32; /** * * * @param doAverage * * @throws RemoteException * @throws VisADException */ public void runInParallel(boolean doAverage) throws VisADException, RemoteException { myMaxThreads = Math.max(myMaxThreads, 1); myMaxThreads = Math.min(myMaxThreads, MAX_THREADS); int max = Math.min(myMaxThreads, runnables.size()); int min = max; running = true; //If we are not running in parallel then just run in this thread //so we minimize any side effects long t1 = System.currentTimeMillis(); if(max<2) { for (MyRunnable myRunnable : runnables) { try { myRunnable.run(); } catch (Exception exc) { handleException(exc); } checkErrors(); } } else { ThreadPool pool; try { pool = new ThreadPool("thread util", min, max); } catch (Exception exc) { throw new RuntimeException(exc); } for (MyRunnable myRunnable : runnables) { runnableStarted(); final MyRunnable theRunnable = myRunnable; Runnable runnable = new Runnable() { public void run() { try { theRunnable.run(); } catch (Exception exc) { handleException(exc); } finally { runnableStopped(); } } }; pool.queue(runnable); } try { pool.waitForTasks(); checkErrors(); } finally { try { pool.stopThreads(); } catch (Exception ignoreThis) {} } } running = false; long t2 = System.currentTimeMillis(); if(debug) { System.err.println( name + " time:" + (t2 - t1) + " max threads:" + myMaxThreads); } if (doAverage && false) { Integer[] tuple = times.get(new Integer(myMaxThreads)); if (tuple == null) { times.put(new Integer(myMaxThreads), tuple = new Integer[] {0, 0}); } tuple[0] = new Integer(tuple[0].intValue() + 1); tuple[1] = new Integer(tuple[1].intValue() + (int)(t2 - t1)); System.err.print(" times:"); for (Enumeration keys = times.keys(); keys.hasMoreElements(); ) { Integer maxThreads = (Integer)keys.nextElement(); Integer[] values = times.get(maxThreads); System.err.print( " #:" + maxThreads + " avg:" + (int)(values[1].intValue() / (double)values[0].intValue())); } System.err.println(""); } } /** * * * @throws RemoteException * @throws VisADException */ private void checkErrors() throws VisADException, RemoteException { try { if (visadExceptions.size() > 0) throw visadExceptions.get(0); if (remoteExceptions.size() > 0) throw remoteExceptions.get(0); if (runtimeExceptions.size() > 0) throw runtimeExceptions.get(0); } finally { running = false; } } /** * Return the list of any exceptions that were thrown when running the threads * * @return The exceptions that were thrown */ public List<Exception> getExceptions() { List<Exception> exceptions = new ArrayList<Exception>(); exceptions.addAll(visadExceptions); exceptions.addAll(remoteExceptions); exceptions.addAll(runtimeExceptions); return exceptions; } /** * MyRunnable */ public interface MyRunnable { /** * * * @throws Exception */ public void run() throws Exception; } public static final void doWork(int amt,int[]A) { long x=0; long y=0; long work = ((long)amt)*2000000L; /* for(int j=0;j<amt;j++) { for(int i=0;i<A.length;i++) { A[i] = 0; } }*/ for(long i=0;i<work;i++) { x++; } } /** * Main method for testing * * @param args args */ public static void main(String[] args) throws Exception { int numberOfProcessors = Runtime.getRuntime().availableProcessors(); int myCnt = (args.length>0?new Integer(args[0]).intValue():2); // for(int j=0;j<1000;j++) { for(myCnt=1;myCnt<20;myCnt+=1) { visad.util.ThreadManager threadManager = new visad.util.ThreadManager(); final int amt = 2000; // final int cnt = (args.length>0?new Integer(args[0]).intValue():2); final int cnt = myCnt; final int [] A = new int[10000000]; for(int i=0;i<cnt;i++) { threadManager.addRunnable(new visad.util.ThreadManager.MyRunnable() { public void run() throws Exception { doWork(amt/cnt,A); } }); } long t1 = System.currentTimeMillis(); threadManager.runInParallel(cnt); // threadManager.runSequentially(); long t2 = System.currentTimeMillis(); long time = t2-t1; // System.err.println (cnt +" time:" + time); } // } } }