6.9 תהליכונים, א סיכרוניות הרצאה
תכנות רב תהליכונים - Multithreading Programming¶
- תכנות רב תהליכונים, מאפשר לנו לכתוב תוכנה שיכולה להריץ כמה דברים במקביל.
- תהליכון או באנגלית thread זה יחידת עיבוד, תהליכון יכול להריץ קוד, כך שתכנות רב תהליכונים זה כאשר לתוכנה שלנו יש מספר תהליכונים - מספר thread-ים
מודול threading¶
- המודול מאפשר לנו לכתוב תוכנות עם מספר תהליכונים
- מודול מובנה
import threading import time def print_numbers(): for i in range(5): time.sleep(1) print(f"Thread 1: {i}") def print_letters(): for letter in 'ABCDE': time.sleep(1) print(f"Thread 2: {letter}") # Create two threads thread1 = threading.Thread(target=print_numbers) thread2 = threading.Thread(target=print_letters) # Start the threads thread1.start() thread2.start() # Wait for both threads to finish thread1.join() thread2.join() print("Multithreading Example Completed") - אפשר לראות שיצרנו 2 תהליכונים שונים, שני התהליכונים ירוצו באותו הזמן - שני הפונקציות
print_numbersו -print_lettersירוצו באותו הזמן
ביטחות עם תהליכונים¶
- בתכנות עם מספר תהליכונים, יכול לקרות מצב שבו שני תהליכונים מנסים לשנות את אותו משתנה בו זמנית וזה יכול לגרום לדברים לא צפויים. למשל ראו את הדוגמה הבאה:
import threading shared_variable = 0 def increment_shared_variable(): global shared_variable for _ in range(1000000): shared_variable += 1 def decrement_shared_variable(): global shared_variable for _ in range(1000000): shared_variable -= 1 # Create two threads thread1 = threading.Thread(target=increment_shared_variable) thread2 = threading.Thread(target=decrement_shared_variable) # Start the threads thread1.start() thread2.start() # Wait for both threads to finish thread1.join() thread2.join() print("Shared Variable:", shared_variable) - יש פה שני תהליכונים שמנסים לשנות את
shared_variableבו זמנית, והמחשב שלנו לא טוב בלבצע 2 פעולות שקשורת לאותו משתנה בו זמנית. ועלול לקרות מצב שבו המחשב יריץ רק את אחת הפעולות ולא את שניהן, זה יכול לגרום לבאגים, באגים קשים לפתירה, לא משהו שנרצה להתקל בו. - אז מה עושים? אנחנו יכולים לנעול את הפעולה שאנחנו עושים עם מנעול, ורק כאשר התהליכון מסיים את הפעולה הוא פותח את המנעול, ורק אז התהליכון השני יוכל לבצע פעולה בעצמו - ראו בקוד הבא:
import threading shared_variable = 0 lock = threading.Lock() # Creating a new lock def increment_shared_variable(): global shared_variable for _ in range(1000000): with lock: # Only when the lock is free, the thread can access the `shared_variable` shared_variable += 1 def decrement_shared_variable(): global shared_variable for _ in range(1000000): with lock: # Only when the lock is free, the thread can access the `shared_variable` shared_variable -= 1 # Create two threads thread1 = threading.Thread(target=increment_shared_variable) thread2 = threading.Thread(target=decrement_shared_variable) # Start the threads thread1.start() thread2.start() # Wait for both threads to finish thread1.join() thread2.join() print("Shared Variable:", shared_variable) - המנעול הזה נקרא mutex, והוא מאפשר למחשב להריץ איזשהו קטע קוד בבטחה - לא יפריעו לו באמצע עד שלא סיים את הפעולה.
- קיימים עוד סוגים של מנעולים כמו mutex, כמו semaphore - מוזמנים לקרוא עליהם.
- כשאנחנו כותבים קוד עם מספר thread-ים אנחנו נשתמש במנעולים.
תכנות אסיכרוני¶
-
בדיוק כמו תכנות עם מספר תהליכונים, תכנות אסיכרוני מאפשר לנו להריץ דברים בו זמנית, ההבדל בא לידי ביטוי באיך שהמחשב עושה את זה מאחורי הקלעים, מוזמנים לחפש בעצמכם באינטרנט את ההבדלים.
-
מודול
asyncio - מאפשר לנו לכתוב קוד אסיכרוני בפייתון
- מודול מובנה.
import asyncio async def print_numbers(): for i in range(5): await asyncio.sleep(1) print(f"Async Task 1: {i}") async def print_letters(): for letter in 'ABCDE': await asyncio.sleep(1) print(f"Async Task 2: {letter}") # Create an event loop async def main(): task1 = asyncio.create_task(print_numbers) task2 = asyncio.create_task(print_letters) await asyncio.gather(task1, task2) # Run the event loop asyncio.run(main) - פונקציות שמסומנות עם "async" הן פונקציות אסכרוניות, הן פונקציות שיכולות לרוץ בצורה אסיכרונית - זה אומר שברגע שנקרא להן הן ירוצו והקוד ימשיך לרוץ - ממש כמו thread-ים.
- כשאנחנו קוראים לפונקציה אסיכרונית עם "await", אנחנו בעצם מחכים לפונקציה שתסיים לרוץ ונעצור את הקוד עד שהיא חוזרת למורות שהיא אסכרונית.
מודול aiohttp¶
- ספרייה שמאפשרת לנו לשלוח בקשות אינטרנט (בקשות לאתרים) בצורה אסכרונית
- הריצו -
pip install aiohttp
import aiohttp import asyncio async def fetch_url(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.text() async def main(): urls = ['https://www.example.com', 'https://www.example.org', 'https://www.example.net'] tasks = [fetch_url(url) for url in urls] responses = await asyncio.gather(*tasks) for url, response in zip(urls, responses): print(f"URL: {url}, Response Length: {len(response)}") asyncio.run(main()) - הספרייה נותנת לכם את האפשרות לכתוב קוד שניגש לאתרים בצורה מאוד מהירה, תחשבו שאתם יכולים לשלוח בו זמנית למספר אתרים בקשה, ולחכות לתשובה מכולם ביחד במקום לשלוח בקשה אחד אחד.