liveBook · Manning

Independence

As explained in Grokking Concurrency

Task independence is the property that tasks can execute in any order without depending on one another's intermediate results.

You can chop carrots while a pot of broth cooks, but you cannot finish the soup before its ingredients are ready. Programming tasks have similar relationships. Tasks are independent when they can run in any order, on different processing resources, and still produce the same result. One task does not need another task’s intermediate result. section 7.1 section 2.8

To find independent work, break a problem into tasks and ask what each task must wait for. A task dependency graph makes this visible: nodes are tasks, and edges show dependencies. Tasks with no dependency between them, such as making broth and chopping vegetables, can proceed concurrently. A dependent step must wait. section 7.1

Independence makes parallel execution possible, but it is not enough by itself: the computer also needs processing resources that can run tasks simultaneously. If tasks depend on previous results, synchronization makes them wait and can limit the benefit. Tasks needing little or no synchronization are called embarrassingly parallel. section 2.7 section 2.8

Check password candidates separately

Each candidate password can be checked against a hash without first checking the other candidates. Split the candidates into ranges, assign ranges to processing resources, and check them in parallel. The checks have no dependency on one another; the goal is to find the matching password. section 2.10

Count votes, then combine

Give each worker a separate pile of ballots to count. The workers can produce their summaries independently, but the final total depends on those summaries. This is the fork/join pattern: fork independent work, then join its results at a synchronization point. Independence may apply to one stage of a problem without applying to the whole problem. section 7.7

# Chapter 7/count_votes/count_votes_concurrent.py 
import typing as T
import random
from multiprocessing.pool import ThreadPool

Summary = T.Mapping[int, int]

def process_votes(pile: T.List[int], worker_count: int = 4) -> Summary:
    vote_count = len(pile)                                          #1
    vpw = vote_count // worker_count                                #1
                                                                    #1
    vote_piles = [                                                  #1
        pile[i * vpw:(i + 1) * vpw]                                 #1
        for i in range(worker_count)                                #1
    ]                                                               #1
                                                                    #1
    with ThreadPool(worker_count) as pool:                          #1
        worker_summaries = pool.map(process_pile, vote_piles)       #1

    total_summary = {}                                              #2
    for worker_summary in worker_summaries:                         #2
        print(f"Votes from staff member: {worker_summary}")         #2
        for candidate, count in worker_summary.items():             #2
            if candidate in total_summary:                          #2
                total_summary[candidate] += count                   #2
            else:                                                   #2
                total_summary[candidate] = count                    #2

    return total_summary

def process_pile(pile: T.List[int]) -> Summary:
    summary = {}
    for vote in pile:
        if vote in summary:
            summary[vote] += 1
        else:
            summary[vote] = 1
    return summary

if __name__ == "__main__":
    num_candidates = 3
    num_voters = 100000
    pile = [random.randint(1, num_candidates) for _ in range(num_voters)]
    counts = process_votes(pile)
    print(f"Total number of votes: {counts}")

The Python vote-counting example divides a pile into worker piles and uses a ThreadPool to process them, making the independent stage concrete. section 7.7

Independence can also separate different kinds of work: searching emails and displaying them can be distinct tasks, as can sending and receiving emails. Look for genuine independence rather than merely adding workers. If workers must wait for the same resource or for one another’s results, the extra coordination may slow the work instead of helping. section 7.2 section 2.8

How Grokking Concurrency covers it

How deep it goes
Defines order-independent execution and shows dependency analysis, synchronization limits, and fork/join; defers deeper decomposition and synchronization treatment.
Examples
Uses soup preparation, password checking, email features, and ballot counting to distinguish independent work from dependent steps.
What it assumes
Introduces independence and hardware support as parallel-computing requirements, then builds dependency analysis from everyday tasks.
What you can do after
Apply it by mapping task dependencies, separating work that can proceed independently, and identifying where results must be joined.
How it connects
Connects task decomposition, dependency graphs, parallel computing, synchronization, and the fork/join pattern.

Also explained in

Compare how 2 books explain independence →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Grokking Concurrency. The section links point to the parts of the book it draws on.