آموزش پایتون - همگامسازی نخها
در پایتون، هنگامی که چند نخ (Thread) به صورت همزمان با منابع مشترک کار میکنند، همگامسازی دسترسی آنها برای حفظ یکپارچگی دادهها و صحت عملکرد برنامه اهمیت بالایی دارد. همگامسازی نخها در پایتون را میتوان با استفاده از ابزارهای پایه همگامسازی (Synchronization Primitives) مختلفی که توسط ماژول threading ارائه میشوند، مانند قفلها (Locks)، شرطها (Conditions)، سیمافورها (Semaphores) و سدها (Barriers) انجام داد تا دسترسی به منابع مشترک کنترل شده و اجرای چند نخ هماهنگ شود.
در این آموزش، با ابزارهای پایه همگامسازی مختلفی که ماژول threading پایتون ارائه میدهد آشنا خواهیم شد.
همگامسازی نخها با استفاده از Lockها
شیء قفل (Lock) در ماژول threading پایتون، سادهترین ابزار پایه همگامسازی را ارائه میدهد. قفلها به نخها اجازه میدهند در اطراف بخشهای حساس (Critical Sections) کد، قفل را دریافت و آزاد کنند و مطمئن شوند که در هر لحظه تنها یک نخ میتواند کد محافظتشده را اجرا کند.
یک قفل جدید با فراخوانی متد Lock() ساخته میشود که یک شیء قفل برمیگرداند. قفل را میتوان با استفاده از متد acquire(blocking) دریافت کرد که نخها را مجبور میکند به صورت همگام (Synchronous) اجرا شوند. پارامتر اختیاری blocking به شما امکان میدهد کنترل کنید که آیا نخ برای دریافت قفل منتظر بماند یا خیر، و قفل با استفاده از متد release() آزاد میشود.
مثال
مثال زیر نحوه استفاده از قفلها (متد threading.Lock()) را برای همگامسازی نخها در پایتون نشان میدهد و تضمین میکند که چند نخ به صورت ایمن و درست به منابع مشترک دسترسی پیدا کنند.
import threading
counter = 10
def increment(theLock, N):
global counter
for i in range(N):
theLock.acquire()
counter += 1
theLock.release()
lock = threading.Lock()
t1 = threading.Thread(target=increment, args=[lock, 2])
t2 = threading.Thread(target=increment, args=[lock, 10])
t3 = threading.Thread(target=increment, args=[lock, 4])
t1.start()
t2.start()
t3.start()
# Wait for all threads to complete
for thread in (t1, t2, t3):
thread.join()
print("All threads have completed")
print("The Final Counter Value:", counter)
خروجی
هنگامی که کد بالا اجرا شود، خروجی زیر را تولید میکند −
All threads have completed
The Final Counter Value: 26
اشیاء Condition برای همگامسازی نخهای پایتون
متغیرهای شرط (Condition Variables) به نخها امکان میدهند تا زمانی که توسط نخ دیگری مطلع (Notify) شوند، منتظر بمانند. این متغیرها برای ایجاد ارتباط بین نخها مفید هستند. متد wait() برای متوقف کردن یک نخ استفاده میشود تا زمانی که از طریق notify() یا notify_all() توسط نخ دیگری مطلع شود.
مثال
این مثال نشان میدهد که چگونه اشیاء Condition میتوانند نخها را با استفاده از متدهای notify() و wait() همگامسازی کنند.
import threading
counter = 0
# Consumer function
def consumer(cv):
global counter
with cv:
print("Consumer is waiting")
cv.wait() # Wait until notified by increment
print("Consumer has been notified. Current Counter value:", counter)
# increment function
def increment(cv, N):
global counter
with cv:
print("increment is producing items")
for i in range(1, N + 1):
counter += i # Increment counter by i
# Notify the consumer
cv.notify()
print("Increment has finished")
# Create a Condition object
cv = threading.Condition()
# Create and start threads
consumer_thread = threading.Thread(target=consumer, args=[cv])
increment_thread = threading.Thread(target=increment, args=[cv, 5])
consumer_thread.start()
increment_thread.start()
consumer_thread.join()
increment_thread.join()
print("The Final Counter Value:", counter)
خروجی
با اجرای برنامه بالا، خروجی زیر تولید خواهد شد −
Consumer is waiting
increment is producing items
Increment has finished
Consumer has been notified. Current Counter value: 15
The Final Counter Value: 15
همگامسازی نخها با استفاده از متد join()
متد join() در ماژول threading پایتون برای انتظار تا زمان پایان اجرای تمام نخها استفاده میشود. این یک روش مستقیم و ساده برای همگامسازی نخ اصلی (Main Thread) با اتمام کار سایر نخها است.
مثال
این مثال همگامسازی نخها با استفاده از متد join() را نشان میدهد تا مطمئن شویم نخ اصلی پیش از ادامه کار، منتظر میماند تا تمام نخهای شروعشده کار خود را تمام کنند.
import threading
import time
class MyThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
print_time(self.name, self.counter, 3)
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threads = []
# Create new threads
thread1 = MyThread(1, "Thread-1", 1)
thread2 = MyThread(2, "Thread-2", 2)
# Start the new Threads
thread1.start()
thread2.start()
# Join the threads
thread1.join()
thread2.join()
print("Exiting Main Thread")
خروجی
با اجرای برنامه بالا، خروجی زیر تولید خواهد شد −
Starting Thread-1
Starting Thread-2
Thread-1: Mon Jul 1 16:05:14 2024
Thread-2: Mon Jul 1 16:05:15 2024
Thread-1: Mon Jul 1 16:05:15 2024
Thread-1: Mon Jul 1 16:05:16 2024
Thread-2: Mon Jul 1 16:05:17 2024
Thread-2: Mon Jul 1 16:05:19 2024
Exiting Main Thread
ابزارهای پایه همگامسازی اضافی
علاوه بر ابزارهای همگامسازی بالا، ماژول threading پایتون موارد زیر را نیز ارائه میدهد: −
- قفلهای بازگشتی (RLocks یا Reentrant Locks): نوعی از قفلها که به یک نخ اجازه میدهند پیش از آزاد کردن قفل، همان قفل را چند بار دریافت کند؛ این ویژگی در توابع بازگشتی یا فراخوانیهای تو در توی توابع کاربرد دارد.
- سیمافورها (Semaphores): مشابه قفلها اما دارای یک شمارنده هستند. نخها میتوانند سیمافور را تا حد معینی که در زمان مقداردهی اولیه تعریف شده است دریافت کنند. سیمافورها برای محدود کردن دسترسی به منابعی با ظرفیت ثابت مفید هستند.
- سدها (Barriers): به تعداد مشخصی از نخها اجازه میدهند در یک نقطه سد (Barrier Point) همگام شوند و تنها زمانی به اجرا ادامه دهند که همه نخها به آن نقطه رسیده باشند. سدها برای هماهنگکردن گروهی از نخها که همگی باید فاز خاصی از اجرا را قبل از پیشروی هر یک از آنها به پایان برسانند، کاربرد دارند.
پیشرفت مطالعه: وضعیت خواندهشدن این درس فقط در همین مرورگر ذخیره میشود؛ بدون نیاز به حساب کاربری.