پاسخ تمرینات مفهوم GIL در پایتون

Please login to bookmark Close


پاسخ تمرین ۱

GIL (مخفف Global Interpreter Lock) مکانیزمی در پایتون است که مانع از اجرای واقعاً موازی تردها می‌شود؛ یعنی در هر لحظه فقط یکی از تردهای یک پردازه اجازه‌ی اجرا دارد، نه چند تا هم‌زمان.

پاسخ تمرین ۲

حتی اگر فقط یک ترد در هر لحظه اجرا شود، جابه‌جایی سریع بین تردها ارزش‌افزوده ایجاد می‌کند. در دانلود منیجر، یک ترد بخشی از فایل را دانلود می‌کند و ترد دیگر درصد پیشرفت را به‌روزرسانی می‌کند؛ این دو باید با هم پیش بروند. اگر برنامه تک‌تردی باشد، باید مدام دانلود را متوقف کند تا درصد را نمایش دهد؛ اما جابه‌جایی خودکار بین تردها این هماهنگی را فراهم می‌کند (به‌ویژه چون ترد در حال انتظارِ I/O، اجرا را به دیگری می‌دهد).

پاسخ تمرین ۳

چون افزایش یک متغیر معمولاً از سه گام جدا (خواندن مقدار فعلی، تغییر آن، نوشتن مقدار جدید) تشکیل شده است. GIL از اجرای موازی جلوگیری می‌کند اما می‌تواند وسط این سه گام ترد را عوض کند و اجازه‌ی دسترسی به همان متغیر را به ترد دیگری بدهد. برای جلوگیری از این تداخل باید این مراحل را با یک قفل به‌صورت اتمیک اجرا کرد.

پاسخ تمرین ۴

هدف اصلی سازندگان پایتون سادگی بود؛ GIL مدیریت تردها و جلوگیری از تداخل درون مفسر را ساده‌تر می‌کند. غیرفعال‌کردن آن (از پایتون ۳.۱۳ ممکن است) خطرناک تلقی می‌شود چون مسئولیت کاملِ هماهنگی بین تردها و جلوگیری از تداخل به عهده‌ی برنامه‌نویس می‌افتد، که به دانش عمیق در برنامه‌نویسی چندتردی نیاز دارد.

پاسخ تمرین ۵

کار I/O-bound بیشترِ وقتش را صرف انتظار می‌کند (شبکه، فایل، دیتابیس)؛ اینجا threading مناسب است چون ترد در حال انتظار GIL را آزاد می‌کند و ترد دیگر کار می‌کند. کار CPU-bound تمام‌وقت پردازنده را درگیر می‌کند؛ اینجا تردها به‌خاطر GIL سریع‌تر نمی‌شوند و باید از multiprocessing استفاده کرد که هر پردازه GIL مستقل خودش را دارد.

پاسخ تمرین ۶

import time
from threading import Thread

def fetch():
    time.sleep(2)          # mostly waiting (I/O)

start = time.time()
threads = [Thread(target=fetch) for _ in range(4)]
for t in threads: t.start()
for t in threads: t.join()
print("%.1fs" % (time.time() - start))   # ~2s, not ~8s

پاسخ تمرین ۷

import time
from multiprocessing import Process

def heavy():
    s = 0
    for i in range(20_000_000):
        s += i

if __name__ == '__main__':
    start = time.time()
    p1 = Process(target=heavy)
    p2 = Process(target=heavy)
    p1.start(); p2.start()
    p1.join(); p2.join()
    print("%.2fs" % (time.time() - start))   # real parallelism across cores

راه‌حل جایگزین: استفاده از concurrent.futures.ProcessPoolExecutor که مدیریت پردازه‌ها را ساده‌تر می‌کند.

Please login to bookmark Close
نظرات

دیدگاهتان را بنویسید

فهرست مطالب

سرفصل دوره

تمرین

این قسمت تمرین ندارد!

پاسخ تمرین ها

هنوز برای تمرین‌های این قسمت پاسخی ثبت نشده است!

اشتراک گذاری

چرا بهتره از فیلترشکن استفاده کنید؟

من همه ویدئو ها و پادکست های کُدباز رو توی یوتیوب و ساندکلود و پلتفرم هایی آپلود می‌کنم که اغلب فیلتر هستند.

اغلب آموزش‌ها ویدئو و پادکست دارند. پس اگر می‌خواهید از محتوای سایت بیشترین استفاده رو ببرید نیاز به فیلتر شکن دارید.

توجه داشته باشید که برای خرید از فروشگاه بهتره فیلتر شکن رو خاموش کنید.

تنظیمات

انتخاب زبان
تغییر تم