Understanding Concurrency
Concurrency
The ability to execute multiple tasks simultaneously.
In computer science, concurrency often involves breaking down a problem into smaller, independent tasks that can be executed at the same time, either by multiple processors or through time-sharing on a single processor.
- Concurrency is not the same as parallelism.
- While concurrency involves managing multiple tasks at once, parallelism specifically refers to executing multiple tasks simultaneously on different processors.
Advantages of Concurrency
Improved performance: By dividing tasks and processing them simultaneously and independently, this approach can significantly reduce the time required to complete a program.
In a web server, handling multiple client requests concurrently ensures faster response times and better user experience.
Resource utilisation: Concurrent processing maximises CPU utilisation by ensuring that processors are not idle.
While one task waits for I/O operations to complete, another task can use the CPU for computation.
Scalability: Concurrent processing allows systems to scale by adding more processors or cores, accommodating increased demand.
Cloud computing platforms use concurrency to distribute workloads across multiple servers.
Responsiveness: In interactive applications, concurrent processing ensures that the user interface remains responsive while background tasks execute.
A video editing application can render previews concurrently while the user continues to edit.
Disadvantages of Concurrency
Complexity: Designing and implementing concurrent systems is more complex than sequential systems.
Managing synchronisation and communication between threads requires careful planning.
Data consistency: Concurrent access to shared data can lead to race conditions, where the outcome depends on the order of execution.
Two threads updating a shared counter without proper synchronisation may result in incorrect values.
Deadlocks and starvation
- Deadlocks: Occur when two or more tasks wait indefinitely for resources held by each other.
- Starvation: Occurs when a task is perpetually denied access to resources.
In a database system, improper locking mechanisms can lead to deadlocks, halting progress.
Overhead: Frequent context switching between tasks can introduce overhead, reducing the efficiency gains of concurrency.
In systems with limited resources, excessive context switching can degrade performance.
Identifying Concurrent Parts of a Solution
A real-world example: preparing a meal
- Concurrent tasks: Boiling pasta and preparing sauce can be done simultaneously on separate burners.
- Sequential tasks: Plating the meal must wait until both the pasta and sauce are ready.
Task independence: Tasks that do not rely on the results of other tasks can be executed concurrently.
In a web browser, downloading images and rendering HTML can occur simultaneously because they are independent of each other.
- When identifying concurrent tasks, look for operations that do not share data or resources.
- This minimises the risk of conflicts and ensures smooth execution.
Data Parallelism: Data parallelism involves dividing a large dataset into smaller chunks and processing each chunk concurrently.
In image processing, applying a filter to different sections of an image can be done concurrently, as each section is processed independently.
- Think of data parallelism like a team of painters working on different sections of a wall.
- Each painter works independently, but together they complete the job faster.
Task decomposition: Breaking down a complex task into smaller, independent sub-tasks that can be executed concurrently.
In a video game, rendering graphics, processing user input, and updating game physics can be handled as separate concurrent tasks.
Task decomposition is a key aspect of modular programming, where a problem is divided into smaller, manageable parts.
Asynchronous Operations: Tasks that can be initiated and then left to complete independently, allowing other tasks to proceed without waiting.
In a web application, fetching data from a server can be done asynchronously, allowing the user interface to remain responsive.
- Avoid assuming that all tasks can be executed concurrently.
- Some tasks may have dependencies that require sequential execution.
- Tools and techniques for concurrent implementation:
- Threads and processes: Use threads or processes to execute tasks concurrently.
- Synchronisation mechanisms: Use locks, semaphores, and other tools to manage access to shared resources.
- Asynchronous programming: Use async/await patterns to handle tasks that can run independently.
- When implementing concurrency, start by identifying independent tasks and use synchronisation mechanisms only when necessary to minimise complexity.
- What is the difference between concurrency and parallelism?
- What are the main advantages of concurrency?
- What challenges can arise when implementing concurrency?
- How can you identify parts of a problem that are suitable for concurrent execution?