To be able to use the profiler and debugger, i worked directly on the gereated java file, using eclipse.
here is the source code : http://www.electrosphere.fr/java-source/PartNG_7.java
If it can be done in eclipse, it can be done in processing IDE, right ?
The application was simplified (a lot) to be able to easily keep track of bugs. (which isn't easy in multithread).
i coded for 20h in a row yesterday, had to learn everything about thread in java, and tried many many possible solution.
i finally found the the safest and simpliest one using the latest java version.
i use : Executors.newFixedThreadPool(4);
it will provide the hability to reuse thread instead of creating new thread at every frame.
then i created a "Callable" class (called "myThread" ...).
The problem was :
- how do i give arguments to a thread.
- how do i wait all 4 threads before continuing a new draw loop.
- how do i retrieve the result.
i still use the "point(x,y)" method beacuse it's handy and because i'm planning to do that in 3D do. And the pixel[] tricks won't work in 3D
The "point(x,y)" is absolutly not threadsafe. So i had to do all the drawing in a single thread, once the 4 thread finished the processing. And so... i had to find a way to do something like :
- main loop : i'm waiting for you guys.
- thread 1 : i finished my work, here is the list of point to draw.
- thread 2,3,4 : yeah, me too. here is my list.
- main loop : thx. i'll draw the stuff myself.
Using Callable, the thread can return anything.
i choosed to return an ArrayList<Vector3D>.
To be able the retrieve a value (or a list of value, or whatever), i had to use a "Future" ("FutureTask" can be used to, afaik).
Future is a class providing the ability to pause, cancel, request a return value, and some shiny stuff from thread. (and also throw an exception when something went wrong).
it's all in the new : java.util.concurrent.* package.
anyway, in my draw loop, i create the thread :
myThread thread1 = new myThread(particles, NUM_PARTICLES, cRed);
myThread thread2 = new myThread(particles2, NUM_PARTICLES, cBlue);
myThread thread3 = new myThread(particles3, NUM_PARTICLES, cRed);
myThread thread4 = new myThread(particles4, NUM_PARTICLES, cBlue);
and tell them to start, the executor return the "Future" handle.
Future future1 = executor.submit(thread1);
Future future2 = executor.submit(thread2);
Future future3 = executor.submit(thread3);
Future future4 = executor.submit(thread4);
while they work, i create my 4 ArrayList<Vector3D> to store the result in the main thread.
ArrayList<Vector3D> myResult1 = new ArrayList<Vector3D>(NUM_PARTICLES);
ArrayList<Vector3D> myResult2 = new ArrayList<Vector3D>(NUM_PARTICLES);
ArrayList<Vector3D> myResult3 = new ArrayList<Vector3D>(NUM_PARTICLES);
ArrayList<Vector3D> myResult4 = new ArrayList<Vector3D>(NUM_PARTICLES);
now i'm waiting for the thread. the Future.get(); is a blocking operation (there is some non-blocking one, but i wanted a blocking one).
try
{
myResult1 = (ArrayList<Vector3D>)future1.get();
myResult2 = (ArrayList<Vector3D>)future2.get();
myResult3 = (ArrayList<Vector3D>)future3.get();
myResult4 = (ArrayList<Vector3D>)future4.get();
}
catch (Exception e) {
System.err.println (e);
// add some exception handling stuff here.
}
now i have my fully completed result and the thread are waiting for new stuff.
i draw the result :
for(int i = 0; i < NUM_PARTICLES; i++)
{
stroke(cBlue);
point(myResult1.get(i).x, myResult1.get(i).y);
point(myResult2.get(i).x, myResult2.get(i).y);
stroke(cRed);
point(myResult3.get(i).x, myResult3.get(i).y);
point(myResult4.get(i).x, myResult4.get(i).y);
}
and i'm ready for a new loop.
Job done.
it need some kind of improvment.
But, right now, i'm 100% sure that it's all threadsafe. No fancy memory-inconsistancy, dead-lock, or other annoying problems.
i used a simple ArrayList<T> but there is a kind ThreadList specialy created for thrreadsafe operation in the concurrent package. I didn't used it because it wasn't required here. (it's all synchronous and each thread are warranted to have their own Array and nobody trying to read/write their array while working on it.).
a good optimisation could be to create a non-blocking "drawing thread", so the thread could start working on a new set of value while everything is drawn, instead of waiting for the drawing to finish.
Another optimisation is about memory usage.
But i'm not sure how to safely save some memory without creating some kinds of weird problem, or having to use mutex.
I think it could be nice to create a processing thread library. Using Executor and Future.