Computer Science
Grade 11
20 min
9. Concurrent Data Structures: Thread-Safe Queues and Hash Tables
Explore concurrent data structures, such as thread-safe queues and hash tables, and their implementations.
Tutorial Preview
1
Introduction & Learning Objectives
Learning Objectives
Explain why standard data structures like queues and hash tables are not safe for use in multi-threaded programs.
Define and identify a 'race condition' in a code snippet involving a shared data structure.
Describe the concept of a 'lock' or 'mutex' and how it provides mutual exclusion to protect a critical section.
Implement a simple producer-consumer pattern using a thread-safe queue.
Compare and contrast the performance and safety characteristics of standard vs. concurrent hash tables.
Analyze a concurrent programming problem and select the appropriate concurrent data structure (queue or hash table) for the solution.
Imagine two people trying to update the same cell in a spreadsheet at the exact same moment. Whose change g...
2
Key Concepts & Vocabulary
TermDefinitionExample
Thread SafetyA piece of code or a data structure is 'thread-safe' if it functions correctly even when accessed by multiple threads simultaneously, without causing race conditions or data corruption.A standard list is not thread-safe; if two threads try to add an element at the same time, the list's internal size counter might only be incremented once, leading to corrupted data. A 'ConcurrentList' would be thread-safe.
Race ConditionA bug that occurs when the outcome of a program depends on the unpredictable sequence or timing of operations from multiple threads. It often happens when threads read and write to a shared resource without protection.Two threads read a bank balance of $100. Thread A calculates a new balance of $150. Thread B calcu...
3
Core Syntax & Patterns
The Lock-Work-Unlock Pattern
my_lock.acquire()
// --- Critical Section Start ---
// Access or modify the shared resource here
// --- Critical Section End ---
my_lock.release()
This is the fundamental pattern for ensuring mutual exclusion. Before entering a critical section, a thread must 'acquire' a lock. If another thread holds the lock, it will wait. After finishing its work with the shared resource, it MUST 'release' the lock so other threads can proceed.
The Producer-Consumer Pattern
// Producer Thread
while (has_data_to_produce):
item = produce_data()
concurrent_queue.enqueue(item)
// Consumer Thread
while (true):
item = concurrent_queue.dequeue() // This will wait if queue is empty
process(item)
A common concurrency pattern where one or mor...
4 more steps in this tutorial
Sign up free to access the complete tutorial with worked examples and practice.
Sign Up Free to ContinueSample Practice Questions
Challenging
The operation `shared_counter++` is not atomic and can cause a race condition. This is because it is actually a sequence of which three distinct operations?
A.1. Allocate memory, 2. Add one, 3. Free memory
B.1. Read the current value, 2. Modify the value (add one), 3. Write the new value back
C.1. Acquire a lock, 2. Add one, 3. Release the lock
D.1. Check the type, 2. Convert to integer, 3. Perform addition
Challenging
To solve a deadlock where Thread 1 locks A then B, and Thread 2 locks B then A, you impose a global rule: 'All threads must acquire locks in alphabetical order.' How does Thread 2's behavior change to follow this rule?
A.It must now acquire lock A first, and then acquire lock B.
B.It must acquire lock B, release it, then acquire lock A.
C.It must use a `tryLock` on B and then a `tryLock` on A.
D.It must acquire both locks simultaneously in a single atomic operation.
Challenging
A developer implements a 'thread-safe' queue using a standard queue and a lock. Their `enqueue` method is: `lock.acquire(); if (queue.size() < MAX_SIZE) { queue.add(item); } lock.release();`. Their `dequeue` method is: `lock.acquire(); if (!queue.isEmpty()) { item = queue.remove(); } lock.release();`. What is a subtle flaw in this design for a producer-consumer scenario?
A.The lock is held for too long, causing poor performance.
B.Using `queue.size()` is a race condition.
C.If the queue is empty, `dequeue` does nothing and returns, forcing the consumer into a busy-wait loop to check again.
D.The `MAX_SIZE` check should happen after adding the item.
Want to practice and check your answers?
Sign up to access all questions with instant feedback, explanations, and progress tracking.
Start Practicing FreeMore from I. Concurrent and Parallel Programming: Unleashing the Power of Multiple Cores
1. Introduction to Concurrency: Threads and Processes
2. Thread Management: Creation, Termination, and Synchronization
3. Synchronization Primitives: Mutexes, Semaphores, and Condition Variables
4. Deadlocks and Race Conditions: Identifying and Avoiding Problems
5. Parallel Programming Models: Shared Memory vs. Distributed Memory