Created
May 9, 2024 18:22
-
-
Save iklobato/b3233373eddff71f757ff698270cb712 to your computer and use it in GitHub Desktop.
This script demonstrates the use of multiprocessing processes (`Writer`, `Drawer`, and `Assembler`) with synchronization using `Condition` and `Event` objects to execute sequential tasks in a concurrent environment.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import logging | |
from multiprocessing import Process, Condition, Event | |
import time | |
logging.basicConfig( | |
level=logging.DEBUG, | |
format="%(asctime)s (%(levelname)s): [%(processName)s] (%(funcName)s): %(message)s", | |
datefmt="%H:%M:%S", | |
) | |
class Writer(Process): | |
def __init__(self, condition, event): | |
super().__init__(name="Writer") | |
self.condition = condition | |
self.event = event | |
def run(self): | |
with self: | |
logging.info("Writing a part of the project...") | |
time.sleep(2) | |
logging.info("Finished writing.") | |
with self.condition: | |
self.condition.notify_all() | |
self.event.set() # Signal that the writer has finished | |
def __enter__(self): | |
logging.info("Starting work.") | |
return self | |
def __exit__(self, exc_type, exc_value, traceback): | |
logging.info("Work completed.") | |
class Drawer(Process): | |
def __init__(self, condition, event): | |
super().__init__(name="Drawer") | |
self.condition = condition | |
self.event = event | |
def run(self): | |
with self: | |
self.event.wait() # Wait for the writer to finish | |
with self.condition: | |
logging.info("Writer is done! Now starting to draw...") | |
time.sleep(2) | |
logging.info("Finished drawing.") | |
self.condition.notify_all() | |
def __enter__(self): | |
logging.info("Starting work.") | |
return self | |
def __exit__(self, exc_type, exc_value, traceback): | |
logging.info("Work completed.") | |
class Assembler(Process): | |
def __init__(self, condition, event): | |
super().__init__(name="Assembler") | |
self.condition = condition | |
self.event = event | |
def run(self): | |
with self: | |
self.event.wait() # Ensure the writer and drawer have finished | |
with self.condition: | |
self.condition.wait() # Ensure drawer is done | |
logging.info("Drawer is done! Assembling...") | |
time.sleep(2) | |
logging.info("Finished assembling.") | |
def __enter__(self): | |
logging.info("Starting work.") | |
return self | |
def __exit__(self, exc_type, exc_value, traceback): | |
logging.info("Work completed.") | |
if __name__ == '__main__': | |
condition = Condition() | |
event = Event() | |
writer_process = Writer(condition, event) | |
drawer_process = Drawer(condition, event) | |
assembler_process = Assembler(condition, event) | |
writer_process.start() | |
drawer_process.start() | |
assembler_process.start() | |
writer_process.join() | |
drawer_process.join() | |
assembler_process.join() | |
logging.info("!") | |
""" | |
2024-05-09 15:15:45 (INFO): [Writer] (__enter__): Starting work. | |
2024-05-09 15:15:45 (INFO): [Writer] (run): Writing a part of the project... | |
2024-05-09 15:15:45 (INFO): [Drawer] (__enter__): Starting work. | |
2024-05-09 15:15:45 (INFO): [Assembler] (__enter__): Starting work. | |
2024-05-09 15:15:47 (INFO): [Writer] (run): Finished writing. | |
2024-05-09 15:15:47 (INFO): [Writer] (__exit__): Work completed. | |
2024-05-09 15:15:47 (INFO): [Drawer] (run): Writer is done! Now starting to draw... | |
2024-05-09 15:15:49 (INFO): [Drawer] (run): Finished drawing. | |
2024-05-09 15:15:49 (INFO): [Drawer] (__exit__): Work completed. | |
2024-05-09 15:15:55 (INFO): [Assembler] (__exit__): Work completed. | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Python Concurrency Concepts: Processes, Threads, and Synchronization
This guide explains key concepts in Python concurrent programming, focusing on multiprocessing and threading, as demonstrated in the script we've been discussing.
1. Processes and Multiprocessing
What is a Process?
A process is an instance of a computer program that is being executed. It has its own memory space and resources.
Multiprocessing in Python
Python's
multiprocessing
module allows you to create and manage processes. It's useful for CPU-bound tasks and parallel processing on multi-core systems.Key Features:
Using the Process Class
2. Threads and Threading
What is a Thread?
A thread is a lightweight subprocess, the smallest unit of processing that can be scheduled by an operating system. Threads within a process share the same memory space.
Threading in Python
Python's
threading
module is used for creating and managing threads. It's useful for I/O-bound tasks.Key Features:
Example of Threading
3. Synchronization Mechanisms
Condition Objects
Condition
objects are synchronization primitives that allow threads to wait for certain conditions to become true.Key Features:
Usage in the Script:
Event Objects
Event
objects are simple synchronization objects that allow communication between processes or threads.Key Features:
Usage in the Script:
4. Comparing Processes and Threads
Processes:
Threads:
5. When to Use Each
6. Best Practices
with
statements with synchronization primitives to ensure proper acquisition and release.join()
to wait for processes or threads to complete.concurrent.futures
for simpler task management.Conclusion
Understanding these concepts is crucial for effective concurrent programming in Python. The script we analyzed demonstrates how to use processes, conditions, and events to create a synchronized workflow across multiple processes. By mastering these tools, you can write efficient, concurrent Python programs that take full advantage of modern multi-core systems.