به تکه کد زیر توجه کنید:
import multiprocessing
# Shared resource
shared_counter = multiprocessing.Value('i', 0)
def increment_counter(shared_counter):
for _ in range(100000):
shared_counter.value += 1
if __name__ == '__main__':
# Create multiple processes
processes = []
for _ in range(5):
p = multiprocessing.Process(target=increment_counter, args=(shared_counter, ))
p.start()
processes.append(p)
# Wait for all processes to finish
for p in processes:
p.join()
# Print the final value of the shared counter
print("Final value of the counter:", shared_counter.value)توضیح کد بالا:
- خط ۴: یک منبع مشترک درست کرده ایم تا بتوانیم بین چندین process از آن استفاده کنیم.
- خط ۶ تا ۸: یک تابع تعریف کردهایم که یک واحد به مقدار یک منبع مشترک اضافه میکند. در نهایت هر بار که این تابع کامل اجرا شود مقدار منبع مشترک باید صد هزار واحد اضافه شده باشد.
- خط ۱۳ تا ۱۶: ۵ پردازش ایجاد و start کردهایم و آنها را به یک لیست نیز اضافه کرده ایم تا بعدا راحت تر join کنیم.
انتظار میرود که خروجی برابر 500000 باشد اما مقداری کمتر است و هر بار که این تکه کد را اجرا کنید مقدار متفاوتی خواهید داشت.
به این مشکل race condition یا شرایط رقابتی میگویند.
برای حل این مشکل باید از کلاس Lock از ماژول multiprocessing استفاده کرد. بدین منظور با کمی تغییر در کد بالا به کد زیر خواهیم رسید که این مشکل را ندارد.
import multiprocessing
lock = multiprocessing.Lock()
# Shared resource
shared_counter = multiprocessing.Value('i', 0)
def increment_counter(shared_counter, lock):
lock.acquire()
for _ in range(100000):
shared_counter.value += 1
lock.release()
if __name__ == '__main__':
# Create multiple processes
processes = []
for _ in range(5):
p = multiprocessing.Process(target=increment_counter, args=(shared_counter, lock))
p.start()
processes.append(p)
# Wait for all processes to finish
for p in processes:
p.join()
# Print the final value of the shared counter
print("Final value of the counter:", shared_counter.value)توضیح کد بالا:
- خط ۳: یک قفل میسازیم.
- خط ۹: قبل از شروع کار با منبع اشتراکی قفل را میبندیم.
- خط ۱۲: بعد از اتمام کار با منبع اشتراکی قفل را باز میکنیم.
البته میتوان به جای استفاده از acquire و release از دستور with نیز استفاده کرد.
import multiprocessing
lock = multiprocessing.Lock()
# Shared resource
shared_counter = multiprocessing.Value('i', 0)
def increment_counter(shared_counter, lock):
with lock:
for _ in range(100000):
shared_counter.value += 1
if __name__ == '__main__':
# Create multiple processes
processes = []
for _ in range(5):
p = multiprocessing.Process(target=increment_counter, args=(shared_counter, lock))
p.start()
processes.append(p)
# Wait for all processes to finish
for p in processes:
p.join()
# Print the final value of the shared counter
print("Final value of the counter:", shared_counter.value)