1 of 17

Understanding Asynchronous Execution: �Processes, Threads, and Asyncio

CSCI 338: Software Engineering

Fall 2025

2 of 17

Announcements

  • Your first PR due tomorrow night at midnight
  • Next Tuesday: short AsyncIO + MVC Lab

3 of 17

Agenda

  1. Asynchronous logic
  2. Activity
  3. Team Meetings

4 of 17

Before we begin, �review the Activity Instructions and �pull down the latest files

5 of 17

Introduction to Asynchronous Execution

What is it?

  • ???

What benefits does it provide?

  • ???

6 of 17

Two categories of tasks to think about

CPU-Bound Tasks: Limited by the processing power of the CPU. Bottleneck: CPU resources.

  • What are some examples of this kind of task?

I/O-Bound Tasks: Limited by the speed of input/output operations, like reading from a disk, making network requests, or querying a database. Bottleneck: waiting for external resources.

  • What are some examples of this kind of task?

7 of 17

1. Processes

Use when you want true CPU parallelism for computationally expensive tasks.

Pros:

  • True parallel execution (leverages multiple CPU cores)
  • Memory isolation prevents accidental overwrites of data; locking mechanism to avoid race conditions.

Cons:

  • Higher overhead (process creation and communication are expensive)
  • Can have race conditions if you don’t do things correctly.

8 of 17

Demo

9 of 17

Let’s talk about what just happened…

When multiple processes or threads access shared data simultaneously without proper synchronization, they can interfere with each other in unexpected ways. This is called a race condition:

  1. Process A reads the current value (e.g., counter = 5)
  2. Process B reads the same value (counter = 5)
  3. Process A increments and writes back (counter = 6)
  4. Process B increments and writes back (counter = 6) ❌

Result: Both processes thought they incremented the counter, but it only increased by 1 instead of 2!

10 of 17

Avoid race conditions by using locks

  • Use synchronization mechanisms like locks to ensure only one process can access the shared data at a time.
  • Race conditions are one of the most common and tricky bugs in concurrent programming because they're often intermittent and hard to reproduce consistently!

11 of 17

Threads: I/O Bound Tasks

Lightweight execution units sharing the same memory space.

Rule of thumb: only use if asyncio is not available

Pros:

  • Lower overhead than processes
  • Suitable for I/O-bound tasks

Cons:

  • Still more complicated than asyncio (next slide)
  • But some languages don’t support asyncio, so you’ll need to use threads.

12 of 17

Asyncio and Coroutines: I/O Bound Tasks

Async I/O with coroutines (single-threaded, cooperative multitasking)

Pros:

  • Ideal for I/O-bound tasks (e.g., network requests, database calls)
  • Low overhead, as it doesn’t require multiple threads or processes

Cons:

  • Not suitable for CPU-bound tasks
  • Requires an event loop and async-aware libraries

13 of 17

When to use what?

Use Processes when:

  • You need true parallel execution
  • Your task is CPU-bound (e.g., image processing, machine learning)

Use Asyncio when:

  • Your task is I/O-bound (network requests, file I/O)
  • You need high scalability with lightweight coroutines

Use Threads when:

  • Your task is I/O-bound and requires shared memory
  • AsyncIO isn’t available

14 of 17

AsyncIO Concepts: The Event Loop

The event loop continuously monitors and manages the execution of asynchronous tasks in a program.

  • Think of it as a conductor in an orchestra, coordinating multiple musicians (tasks) to ensure they play in sync without waiting on each other unnecessarily.

15 of 17

AsyncIO Concepts: The Event Loop

  • Schedules Tasks: When you create asynchronous tasks, the event loop decides when each task gets to run.�
  • Handles Waiting Efficiently: If a task needs to wait (e.g., await asyncio.sleep(2)), the event loop doesn’t just sit idle — it switches to other tasks while waiting, making better use of time.�
  • Manages Execution Flow: Repeatedly checks for tasks that are ready to run, runs them until they either finish or hit an await, and repeats this process until all tasks are done.

16 of 17

AsyncIO Concepts: Coroutines

async def prepare_drink(customer, drink):

# fake logic to prepare a drink

customers = [

("Alice", "Latte"),

("Bob", "Espresso"),

("Charlie", "Cappuccino")

]

tasks = []

for customer, drink in customers:

tasks.append(prepare_drink(customer, drink))

# Then, wait for all tasks to complete

await asyncio.gather(*tasks)

The for loop creates an array of coroutines – but does not invoke the functions yet.

await asyncio.gather(*tasks) �tells asyncio to schedule all those coroutines to run concurrently, and then wait until all of them complete.

17 of 17

Team Activity - 20 minutes

  1. Logistics:
    1. How are things going? Do you think you’re on schedule?
    2. Are there any communication issues? If so, are there things you can do to address issues?
    3. When should everyone’s first pull request be submitted (suggestion: shoot for TONIGHT)
  2. Is anyone stuck? Does everyone know what they’re supposed to be doing?
  3. Pull down the latest from main and run the app (course_lookup/ui.py). How are things looking?

17