# Concurrent programming

***

Sources:

* [asynchronous vs multiprocessing vs multithreading](https://www.youtube.com/watch?v=0vFgKr5bjWI)

***

### What is concurrency in programming?

**Concurrency** means managing multiple tasks that are in progress during overlapping time periods. It's about **structure** - how you organize your program to handle multiple things.

#### Concurrency includes:

1. **Async/await** (cooperative multitasking)
2. **Multithreading** (preemptive multitasking)
3. **Multiprocessing** (separate processes)

**Parallelism** is a **subset** of concurrency - it's when tasks actually execute at the same physical moment on multiple cores.

```
┌─────────────────────────────────────┐
│         CONCURRENCY                 │
│  (Multiple tasks in progress)       │
│                                     │
│  ┌──────────────────────────────┐  │
│  │  Without Parallelism         │  │
│  │  - Async (single thread)     │  │
│  │  - Threads (with GIL)        │  │
│  └──────────────────────────────┘  │
│                                     │
│  ┌──────────────────────────────┐  │
│  │  With Parallelism            │  │
│  │  - Multiprocessing           │  │
│  │  - Threads (no GIL langs)    │  │
│  └──────────────────────────────┘  │
└─────────────────────────────────────┘
```

#### Concurrency

Multiple tasks making progress in overlapping time periods

* **How**: Task switching, interleaving, or parallel execution
* **Goal**: Better structure, responsiveness, resource utilization

#### Parallelism

Multiple tasks executing at the exact same instant

* **How**: Multiple CPU cores working simultaneously
* **Goal**: Faster computation, higher throughput
* **Note**: Parallelism is a specific *implementation* of concurrency

***

#### All Three Are Concurrent:

**1. Async (Concurrent, not parallel)**

```python
async def main():
    await asyncio.gather(
        fetch_data(1),
        fetch_data(2)
    )
```

* Concurrent: Both tasks in progress
* Not parallel: Single thread switches between them

**2. Threading (Concurrent, potentially parallel)**

```python
threads = [
    Thread(target=work, args=(1,)),
    Thread(target=work, args=(2,))
]
```

* Concurrent: Both threads exist and make progress
* Parallel in other languages: Can run on different cores
* Not parallel in Python (for CPU work): GIL restricts to one at a time

**3. Multiprocessing (Concurrent AND parallel)**

````python
with ProcessPoolExecutor() as executor:
    executor.map(work, [1, 2, 3, 4])
```
````

* Concurrent: Multiple processes in progress
* Parallel: Actually running on different cores simultaneously

***

### Asynchronous programming

When programming, [asynchronous](https://en.wikipedia.org/wiki/Asynchrony_\(computer_programming\)) means that the action is requested, although not performed at the time of the request. It is performed later. **Asynchronous programming** is a **programming model/pattern** for achieving concurrency.

Async code is **cooperative multitasking**. When you `await`, you're saying "I'm waiting for something, let other tasks run." The event loop switches between tasks at these `await` points—all on a single thread.

**The key:** Async only works when your waiting is non-blocking (network I/O, `asyncio.sleep()`, etc.). If you have blocking operations (`time.sleep()`, heavy computation, CPU-bound work), they freeze the single thread, so you need threads or processes. Those blocking operations are synchronous operations.

[About blocking and non-blocking calls](https://superfastpython.com/thread-blocking-call-in-python/)

***

***

***
