Code-Memo

Concurrency Patterns and Best Practices

Thread Safety and Race Conditions

Thread safety refers to ensuring that shared data structures and resources can be accessed and modified correctly by multiple threads without causing inconsistencies or errors.

import threading

counter = 0

def increment():
    global counter
    for _ in range(100000):
        counter += 1

# Create two threads to increment the counter
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)

thread1.start()
thread2.start()

thread1.join()
thread2.join()

print(f"Final counter value: {counter}")  # Output varies due to race condition

Designing Concurrent Data Structures

Concurrent data structures are designed to be safely accessed and modified by multiple threads simultaneously, typically using synchronization mechanisms to prevent race conditions.

Example using Queue:

from queue import Queue
import threading

q = Queue()

def worker():
    while True:
        item = q.get()
        # Process item
        q.task_done()

# Create threads to process items from the queue
for _ in range(5):
    threading.Thread(target=worker, daemon=True).start()

# Add items to the queue
for item in range(10):
    q.put(item)

q.join()  # Wait for all items to be processed

Synchronization Primitives (Locks, Semaphores)

Synchronization primitives are mechanisms used to coordinate access to shared resources and prevent simultaneous access by multiple threads.

Example using threading.Lock:

import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    for _ in range(100000):
        with lock:
            counter += 1

# Create two threads to increment the counter
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)

thread1.start()
thread2.start()

thread1.join()
thread2.join()

print(f"Final counter value: {counter}")  # Output: 200000 (consistent due to lock)

Example using threading.Semaphore:

import threading

semaphore = threading.Semaphore(2)  # Allow two threads to access at a time

def access_resource():
    with semaphore:
        # Access shared resource
        pass

# Create multiple threads to access the resource
threads = []
for _ in range(5):
    t = threading.Thread(target=access_resource)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

Benefits of Concurrency Patterns and Best Practices