I'm actually trying to write an event loop for Nashorn (java 8) so that callbacks from asynchronous operations (threads i launch to, for example, connect to remote services or do long-running calculations) will be put in a queue and executed in sequence (not in parallel). I'm doing it by placing the callback functions on a ConcurrentLinkedQueue and using a ScheduledExecutorService as the loop that checks the queue for a callback to execute.
Works fine, but my questions are:
1) how short an interval can I use without dragging my CPU? I will have multiple of these running and they must be independent of one another. Thus there may be 50 threads all running their own "event loop". This executor attempts to run my runnable every 10ms, for example....
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(<cbRunnable>, 0, 10, TimeUnit.MILLISECONDS);
2) Does this approach have an advantage over:
while (true) {
// check queue and execute any callback...
}
3) Is there a better way?
In computer science, the event loop is a programming construct or design pattern that waits for and dispatches events or messages in a program.
JavaScript has a runtime model based on an event loop, which is responsible for executing the code, collecting and processing events, and executing queued sub-tasks. This model is quite different from models in other languages like C and Java.
The language itself is single-threaded, but the browser APIs act as separate threads. The event loop facilitates this process; it constantly checks whether or not the call stack is empty. If it is empty, new functions are added from the event queue. If it is not, then the current function call is processed.
Event loop is an endless loop, which waits for tasks, executes them and then sleeps until it receives more tasks. The event loop executes tasks from the event queue only when the call stack is empty i.e. there is no ongoing task. The event loop allows us to use callbacks and promises.
The answer totally depends on what you're doing inside this block:
while (true) {
// check queue and execute any callback...
}
If the queue check blocks until an element is available, then this is the "most efficient" way to poll, in terms of CPU usage. If the check does not block, then the calling thread will spin and you'll be running one hardware thread at full capacity for the duration of the loop - however, this eliminates synchronization costs and will give you the absolute best response time. Here are some examples:
Blocking Queue (least taxing on the CPU, but comes at the cost of synchronization)
Queue<T> q = new LinkedBlockingQueue<>();
...
while(true){
T t = q.take();
//t will never be null, do something with it here.
}
Non-blocking Queue (most taxing on the CPU, but no synchronization costs)
Queue<T> q = new LinkedList<>();
...
while(true){
T t;
while((t = q.poll()) == null); //busy polling!
//t will never be null, do something with it here
}
ScheduledExecutorService
Finally ... if you end up using the scheduled executor service, you're forcing some non-zero wait time between polls. This will be almost as efficient CPU-wise when compared to a full-on BlockingQueue implementation, but you're also forced to take a hit on response time, up to the scheduling interval. What is "best" for your application will depend on whether or not you can afford to wait the minimum sleep time between polls (I think you can schedule down the microsecond ...?), or if you need something faster like a busy polling scheme.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With