Concurrency patterns - Half-Sync / Half-Async

The pattern separates asynchronous I/O from the synchronous one. Main thread doesn't block on incoming client requests and long-running operations are offloaded to a dedicated synchronous layer. Processing results are delivered by the means of callbacks.


Application

A decent queuing system is required to handle messaging between the two layers. The challenge lies in preventing race conditions and other concurrency related issues.


Key Components

There might be a number of concurrently running synchronous services. The queueing layer is responsible for thread synchronization.


Advantages



Drawbacks



Example

An ASCII Art generator (credit goes to Evilzone) is not only pleasant to work with, but it is also a suitable candidate for a long-running task. I saw it as a perfect fit for the pattern.
 public class AsciiArtGenerator {  
   ..  
   /**  
    * Converts an image to its ASCII representation.  
    *  
    * @param imgPath  path to the image, relative to /src/main/resources  
    * @param outPath  path to the resulting text file, relative to ./data  
    * @return true, if the conversion succeeds, false otherwise  
    */  
   public boolean convertToAscii(String imgPath, String outPath) {..}  
 }  
The image-to-text conversion is a synchronous blocking task, which might take a while to complete. As such it's bound to run in a background thread.

The front-end of the app is served asynchronously via a non-blocking dispatcher:
 /**  
  * Represents an asynchronous layer, as it forwards 
  * client requests for further processing and returns 
  * immediately. It receives results via notifications.  
  *  
  * @author: Tomas Zezula  
  * Date: 24/08/2014  
  */  
 public class NonBlockingDispatcher {  
   ..  
   /**  
    * Sends a request to the queue and returns instantly.  
    *  
    * @param imgPath  Image path for the ASCII generator  
    * @param outPath  Output path for the ASCII generator  
    */  
   public void dispatch(final String imgPath, final String outPath) {..}  
   
   /**  
    * Captures processing result and notifies the subscribed client  
    *  
    * @param result true, if success, false otherwise  
    */  
   public void onResult(boolean result) {..}  
 }  
Finally, the communication between the dispatcher and the worker thread is mediated by a dedicated queuing channel:
 /**  
  * Queues incoming requests and notifies the dispatcher when the response is ready.  
  *  
  * @author: Tomas Zezula  
  * Date: 24/08/2014  
  */  
 public class WorkQueue {..}  
As usual, the example is accompanied by unit tests proving the core concepts. This time, I kept the tests to a bare minimum, just to highlight the major difference between a naive single-threaded synchronous approach and the slightly more advanced asynchronous implementation.

The app pays tribute to a great actress of the last century. The resulting file (./data/audrey.txt) is best viewed using a minimal font-size.

Source Code

Resources