DEV Community

Somenath Mukhopadhyay
Somenath Mukhopadhyay

Posted on • Originally published at som-itsolutions.blogspot.com on

Half Sync - Half Async design pattern implemented using Julia...

My deep study of the Android AsyncTask framework many years ago - a perfect example of this design pattern by bright Google engineers...

Half Sync - Half AsyncHalf Sync - Half Async

Half Sync - Half Async Pattern

# The Julia Code
struct Task
    id::Int
    payload::Float64
end

function worker(id, ch::Channel)
    for task in ch
        println("Worker $id processing Task $(task.id)")
        result = sum(sin.(1:10^6 .* task.payload))
        println("Worker $id finished Task $(task.id)")
    end
    println("Worker $id shutting down")
end

function async_producer(ch::Channel, n::Int)
    for i in 1:n
        sleep(rand())
        println("Producing Task $i")
        put!(ch, Task(i, rand()))
    end
    close(ch)
end

function run_system(num_tasks=10, num_workers=4)
    ch = Channel{Task}(32)

    @sync begin
        # Producer runs as async task tracked by @sync
        @async async_producer(ch, num_tasks)

        # Workers
        for i in 1:num_workers
            Threads.@spawn worker(i, ch)
        end
    end
end

run_system(20, Threads.nthreads())

Enter fullscreen mode Exit fullscreen mode

1. The Pattern Refresher

Half-Sync/Half-Async splits a system into:

πŸ”Ή Async Layer

  • Non-blocking

  • Event-driven

  • Produces work

πŸ”Ή Sync Layer

  • Blocking / CPU-bound

  • Deterministic execution

  • Processes work

πŸ”Ή Boundary

  • A queue (here: Channel)

  • Decouples the two layers

2. Mapping The Code to the Pattern

πŸ”Έ (A) Boundary β†’ Channel

ch = Channel{Task}(32)

Enter fullscreen mode Exit fullscreen mode

This is the core of the pattern.

πŸ‘‰ It acts as:

  • A thread-safe queue

  • A decoupling buffer

  • A synchronization boundary

Interpretation:

β€œAsync world hands off work to Sync world through a controlled interface.”

(B) Async Layer β†’ async_producer

@async async_producer(ch, num_tasks)

Enter fullscreen mode Exit fullscreen mode

Inside:

for i in 1:n
    sleep(rand())
    put!(ch, Task(i, rand()))
end
close(ch)

Enter fullscreen mode Exit fullscreen mode

Why this is β€œAsync”:

  • @async β†’ cooperative scheduling (non-blocking)

  • sleep(rand()) β†’ simulates unpredictable external events

  • put! β†’ hands off work without doing computation

Conceptual role:

β€œI don’t process. I just observe and emit events.”

(C) Sync Layer β†’ worker

Threads.@spawn worker(i, ch)

Enter fullscreen mode Exit fullscreen mode

Inside:

for task in ch
    result = sum(sin.(1:10^6 .* task.payload))
end

Enter fullscreen mode Exit fullscreen mode

Why this is β€œSync”:

  • take! (via for task in ch) β†’ blocking

  • CPU-heavy computation

  • Runs on real OS threads

Conceptual role:

β€œGive me work. I will process it fully and deterministically.”

(D) Coordination β†’ @sync

@sync begin
    @async async_producer(...)
    Threads.@spawn worker(...)
end

Enter fullscreen mode Exit fullscreen mode

This is not part of the original pattern per se, but in Julia it ensures:

  • The system behaves like a long-running service

  • Main thread waits for both layers

3. End-to-End Flow (Pattern in Action)

Step-by-step:

  1. Async Layer wakes up

  2. Task is enqueued

  3. Sync Layer pulls work

  4. Processing happens

  5. Repeat until channel closes

4. Why This is Half-Sync/Half-Async (Not Just Threads)

Because of strict separation of concerns :

Concern Where handled
Event timing Async layer
Work queueing Channel
Execution Sync layer

πŸ‘‰ The producer never processes

πŸ‘‰ The worker never generates events

That separation is the essence of the pattern

5. Key Properties The Code Achieves

Decoupling

  • Producer speed β‰  Worker speed

  • Buffered via Channel(32)

Backpressure

  • If workers are slow β†’ channel fills β†’ put! blocks

  • Natural flow control

Scalability

Threads.@spawn worker(i, ch)

Enter fullscreen mode Exit fullscreen mode
  • Increase workers β†’ parallelism increases

Clean Shutdown

close(ch)

Enter fullscreen mode Exit fullscreen mode
  • Workers exit automatically via:
for task in ch

Enter fullscreen mode Exit fullscreen mode

6. Subtle but Deep Insight

The system is not just parallel β€” it is:

A streaming system with a controlled execution boundary

This is exactly how:

  • High-performance servers

  • Simulation engines

  • Data pipelines

are designed internally.

Top comments (0)