داستان من بسیار شبیه به شماست اما فقط کمی جذابتر است چون شامل رباتهاست.
Bender, Futurama episode “30% Iron Chef ” بِنِدر، قسمت فوتوراما از "آشپز ۳۰ درصد آهنی"
بیشترین پیغامی که من درباره Asyncio در پایتون ۳ میگیرم این است: این چی هست؟ و من چهکاری میتوانم با این انجام دهم؟ پاسخی که احتمالاً بیش از همه خواهید شنید چیزی شبیه به این است که قابلیتی است که امکان اجرای چند درخواست HTTP را به صورت همزمان در یک برنامه، ایجاد میکند. اما خیلی بیش از این است. Asyncio نیاز دارد که شما روش فکر کردن به ساختار یک برنامه را در ذهن خود تغییر دهید.
داستان زیر زمینهای برای به دست آوردن این درک فراهم میکند. تمرکز اصلی Asyncio بر این است که چگونه میتوان چندین کار را همزمان به بهترین نحو انجام داد؛ و نه فقط هر کار، بلکه بهویژه کارهایی که شامل دورههای انتظار هستند. بینش کلیدی مورد نیاز برای این سبک برنامهنویسی این است که در حالی که منتظر تکمیل این کار هستید، میتوانید کارهای دیگر را انجام دهید.
سال 2051 است، و شما خود را در کسب و کار رستوران پیدا میکنید. اتوماسیون، عمدتاً توسط کارگران ربات، بیشتر اقتصادها را تقویت کردهاست، اما به نظر میرسد که انسانها همچنان از بیرون رفتن برای غذا خوردن لذت میبرند. در رستوران شما، همه کارمندان ربات هستند، البته انساننما، اما بدون تردید روبات. موفقترین تولیدکننده رباتها Threading Inc است و کارگران ربات این شرکت «ThreadBots» نامیده میشوند.
به جز این جزئیات کوچک روباتیک، رستوران شما شبیه یکی از آن مکانهای قدیمی مثلاً در سال 2020 به نظر میرسد و عمل میکند. مهمانان شما به دنبال آن تجربه قدیمی خواهند بود. آنها غذای تازهای را میخواهند که از ابتدا تهیه شده باشد. آنها میخواهند پشت میزها بنشینند. آنها میخواهند برای وعده های غذایی خود صبر کنند - اما فقط کمی. آنها میخواهند در آخر پول بپردازند، و حتی گاهی اوقات میخواهند انعام بگذارند، به خاطر قدیمها.
از آنجایی که در کسب و کار رستوران رباتیک، تازه کار هستید، کاری را انجام میدهید که هر مدیر رستوران دیگری انجام میدهد و ناوگان کوچکی از رباتها را استخدام میکند: یکی برای خوشامدگویی به مهمانان در میز پذیرش (GreetBot)، دیگری برای میزهای انتظار و گرفتن سفارش (WaitBot)، دیگری برای انجام کار پخت و پز (ChefBot)، و یکی برای مدیریت بار (WineBot).
مشتریان گرسنه در جلوی رستوران شما به ThreadBotهای پذیرش میرسند و GreetBot از آنها استقبال میکند. سپس آنها را به یک میز هدایت میکنند، و هنگامی که آنها مینشینند، WaitBot سفارش آنها را میگیرد. سپس WaitBot آن سفارش را روی یک تکه کاغذ به آشپزخانه میآورد (چون شما میخواهید آن تجربه قدیمی را حفظ کنید، یادتان هست؟). ChefBot به سفارش روی برگه نگاه میکند و شروع به تهیه غذا میکند. WaitBot به صورت دورهای بررسی میکند که آیا غذا آماده است یا خیر، و وقتی آماده شد، بلافاصله ظروف را به میز مشتریان میبرد. وقتی میهمانان آماده رفتن هستند، به GreetBot برمیگردند، که صورتحساب را محاسبه میکند، پرداخت آنها را میگیرد و با مهربانی برایشان شبی خوش آرزو میکند.
رستوران شما بسیار محبوب است و به زودی یک پایگاه مشتری بزرگ خواهید داشت. کارمندان ربات شما دقیقاً همان کاری را انجام میدهند که به آنها گفته شده است و در وظایفی که به آنها محول میکنید کاملاً خوب هستند. همه چیز واقعاً خوب پیش میرود، و شما نمیتوانید از این خوشحالتر باشید.
با این حال، با گذشت زمان، متوجه برخی مشکلات میشوید. اوه، این مشکلات جدی نیستند؛ فقط چند چیز که گویا اشتباه هستند. ظاهراً هر صاحب رستوران رباتیک دیگری هم، مشکلات مشابهی دارد. این کمی نگران کننده است که به نظر میرسد هر چه موفقتر میشوید این مشکلات بدتر میشوند.
اگرچه نادر است، اما گاه به گاه برخوردهایی وجود دارد که بسیار ناراحت کننده است: گاهی اوقات، وقتی یک بشقاب غذا در آشپزخانه آماده است، WaitBot قبل از اینکه ChefBot حتی بشقاب را رها کند، آن را میگیرد. این معمولاً با شکستن بشقاب ختم میشود و یک آشفتگی بزرگ ایجاد میکند. البته ChefBot آن را تمیز میکند، اما با این حال، شما فکر میکنید که این روبات های درجه یک میدانند که چگونه با یکدیگر هماهنگتر باشند. در بار نیز این اتفاق میافتد: گاهی اوقات WineBot یک سفارش نوشیدنی در بار میدهد و WaitBot قبل از اینکه WineBot رها کند، آن را میگیرد و در نتیجه شیشه شکسته میشود و Nederburg Cabernet Sauvignon ریخته میشود.
همچنین، گاهی اوقات GreetBot مشتریان جدید را دقیقاً در همان لحظهای مینشاند که Wait-Bot تصمیم گرفته است میز خالی را تمیز کند. برای مشتریان بسیار ناخوشایند است. شما سعی کردهاید منطق تاخیر را به عملکرد تمیز کردن WaitBot یا تاخیر در عملکرد نشستن GreetBot اضافه کنید، اما اینها واقعاً کمکی نمیکنند و برخوردها همچنان رخ میدهند. اما حداقل این اتفاقات نادر است.
خوب، البته قبلاً این طور بود، رستوران شما آنقدر محبوب شده است که مجبور شدهاید چند ThreadBots دیگر استخدام کنید. برای عصرهای بسیار شلوغ جمعه و شنبه، باید یک GreetBot دوم و دو WaitBot دیگر اضافه کنید. متأسفانه، قراردادهای استخدام برای ThreadBots به این معنی است که شما باید آنها را برای کل هفته استخدام کنید، بنابراین این در واقع به این معنی است که در بیشتر موارد در بخش آرام هفته، شما سه Thread-Bot اضافی را حمل میکنید که واقعاً به آنها نیاز ندارید.
مشکل دیگر منابع، علاوه بر هزینه اضافی، این است که شما با این ThreadBotهای اضافی کار بیشتری هم خواهید داشت. خوب بود که فقط چهار ربات را بررسی کنید، اما اکنون به هفت ربات رسیدهاید. پیگیری هفت ThreadBot کار بسیار بیشتری نیاز دارد و از آنجایی که رستوران شما مدام شهرت بیشتری پیدا میکند، شما نگران استفاده از ThreadBotهای بیشتر میشوید. فقط پیگیری کارهایی که هر ThreadBot انجام میدهد، به یک شغل تمام وقت تبدیل میشود. و یک چیز دیگر: این Thread-Bot های اضافی فضای بسیار بیشتری را در داخل رستوران شما مصرف میکنند که با رفت و آمد دائم در اطراف مشتریان، فشاری اضافه برای آنها ایجاد میکند. شما نگران این هستید که اگر نیاز به اضافه کردن رباتهای بیشتری داشته باشید، این مشکل فضا، بدتر خواهد شد. شما میخواهید از فضای رستوران خود برای مشتریان استفاده کنید نه ThreadBotها.
از زمانی که ThreadBots بیشتری اضافه کردید، برخوردها نیز بدتر شده است. اکنون، گاهی اوقات دو WaitBot دقیقاً همان ترتیب را از یک جدول به طور همزمان میگیرند. گویی هر دو متوجه شدند که میز آماده سفارش است و برای گرفتن آن وارد شدند، بدون اینکه متوجه شوند WaitBot دیگر دقیقاً همین کار را انجام میدهد. همانطور که میتوانید تصور کنید، این امر منجر به سفارشهای غذایی تکراری میشود که باعث بار اضافی روی آشپزخانه و افزایش احتمال برخورد هنگام برداشتن بشقابهای آماده میشود. شما نگران این هستید که اگر WaitBotهای بیشتری اضافه کنید، این مشکل ممکن است بدتر شود.
زمان میگذرد.
سپس، در یکی از سرویسهای بسیار بسیار شلوغ جمعه شب، لحظهای منحصر به فرد از وضوح دارید: زمان کند میشود، شفافیت شما را تحت تأثیر قرار میدهد، و تصویری از رستوران خود را میبینید که در زمان منجمد شده است. ThreadBotهای من هیچ کاری انجام نمیدهند! انصافاً هیچ چیز نیست، جز اینکه آنها فقط… منتظرند.
هر یک از سه ربات WaitBot شما در میزهای مختلف منتظر هستند تا یکی از مشتریها در میز خود سفارش خود را بدهد. WineBot قبلاً 17 نوشیدنی آماده کرده است که اکنون منتظر جمع آوری آنها هستند (فقط چند ثانیه طول کشید) و منتظر یک سفارش نوشیدنی جدید است. یکی از GreetBotها به مهمانهای جدیدی خوش آمد گفت و به آنها گفت که برای نشستن باید یک دقیقه صبر کنند و منتظر پاسخ مهمانان است. GreetBot دیگر که اکنون پرداخت کارت اعتباری را برای مهمان دیگری که در حال رفتن است پردازش میکند، در انتظار تایید درگاه پرداخت است. حتی ChefBot که در حال حاضر در حال پختن 35 وعده غذایی است، در واقع هیچ کاری در این لحظه انجام نمیدهد، بلکه فقط منتظر است تا یکی از وعده های غذایی کامل پخته شود تا بتوان آن را در بشقاب چید و به WaitBot تحویل داد.
متوجه میشوید که حتی با وجود اینکه رستوران شما پر از ThreadBot است، و شما حتی در فکر دریافت رباتهای بیشتر (با تمام مشکلاتی که به همراه دارد) هستید، اما مواردی که در حال حاضر دارید، به طور کامل مورد استفاده قرار نمیگیرند.
لحظه میگذرد، اما دریافتی که از این لحظه به دست آمد باقی میماند. روز یکشنبه، یک ماژول جمعآوری داده را به ThreadBotهای خود اضافه میکنید. برای هر ThreadBot، اندازهگیری میکنید که چقدر برای انتظار صرف شده است و چقدر صرف انجام کار شده است. در طول هفته بعد، دادهها جمع آوری میشود. سپس یکشنبه عصر، نتایج را تجزیه و تحلیل میکنید. به نظر میرسد که حتی زمانی که رستوران شما با ظرفیت کامل در حال کار است، سختکوشترین ThreadBot در 98٪ مواقع بیکار است. ThreadBotها به قدری کارآمد هستند که میتوانند هر کاری را در کسری از ثانیه انجام دهند.
به عنوان یک کارآفرین، این ناکارآمدی واقعاً شما را آزار میدهد. شما میدانید که هر صاحب رستوران رباتیک دیگری مانند شما، با بسیاری از مشکلات، تجارت خود را اداره میکند. اما، شما فکر میکنید، مشت خود را روی میز خود میکوبید، "باید راه بهتری وجود داشته باشد!"
بنابراین روز بعد، که دوشنبهای آرام است، چیزی جسورانه را امتحان میکنید: یک ThreadBot را برای انجام همه کارها برنامهریزی میکنید. هر بار که شروع به انتظار میکند، حتی برای یک ثانیه، ThreadBot به جای انتظار، به کار بعدی که باید در رستوران انجام شود، هر چه که باشد، سوئیچ میکند. باورنکردنی به نظر میرسد - فقط یک ThreadBot کار بقیه را انجام میدهد - اما شما مطمئن هستید که محاسبات شما درست است. و علاوه بر این، دوشنبه یک روز آرام است. حتی اگر مشکلی پیش بیاید، تأثیر آن اندک خواهد بود. برای این پروژه جدید، ربات را "LoopBot" مینامید زیرا روی تمام کارهای رستوران حلقه میزند.
برنامهنویسی سختتر از حد معمول بود. فقط این نیست که باید یک ThreadBot را با تمام وظایف مختلف برنامهریزی کنید. شما همچنین مجبور بودید مقداری از منطق زمان جابجایی بین وظایف را برنامهریزی کنید. اما در این مرحله، شما تجربه زیادی در برنامهنویسی این ThreadBotها داشتهاید، بنابراین موفق به انجام آن میشوید.
شما مانند یک شاهین LoopBot خود را تماشا میکنید. در کسری از ثانیه بین ایستگاهها حرکت میکند و بررسی میکند که آیا کاری برای انجام دادن وجود دارد یا خیر. مدت کوتاهی پس از افتتاح، اولین مهمان به میز پذیرش میرسد. LoopBot تقریباً بلافاصله ظاهر میشود و میپرسد که آیا مهمان میخواهد یک میز در نزدیکی پنجره داشته باشد یا نزدیک نوار. اما پس از آن، هنگامی که LoopBot شروع به صبر میکند، برنامهنویسی آن به آن میگوید که به کار بعدی تغییر مکان دهد و این کار خاموش میشود. این یک خطای وحشتناک به نظر میرسد، اما بعد میبینید که وقتی مهمان شروع به گفتن «پنجره لطفا» میکند، LoopBot بازگشته است. پاسخ را دریافت میکند و مهمان را به میز 42 هدایت میکند. و دوباره خاموش میشود، سفارشهای نوشیدنی، سفارشهای غذا، تمیز کردن میز و مهمانهای رسیده را بارها و بارها بررسی میکند.
اواخر عصر دوشنبه، موفقیت چشمگیر را به خود تبریک میگویید. شما ماژول جمعآوری دادهها را در LoopBot بررسی میکنید، و تأیید میکند که حتی با یک ThreadBot که کار هفت ربات را انجام میدهد، زمان بیکاری همچنان حدود 97 درصد است. این نتیجه به شما این اطمینان را میدهد که آزمایش را در بقیه هفته ادامه دهید.
با نزدیک شدن به مراسم شلوغ جمعه، شما به موفقیت بزرگ آزمایش خود فکر میکنید. برای خدمات در طول یک هفته کاری عادی، میتوانید به راحتی حجم کار را با یک LoopBot مدیریت کنید. و شما متوجه چیز دیگری شدهاید: دیگر هیچ برخوردی نمیبینید. این منطقی است؛ از آنجایی که تنها یک LoopBot وجود دارد، نمیتواند با خودش اشتباه گرفته شود. دیگر خبری از سفارشهای تکراری برای رفتن به آشپزخانه نیست، و دیگر در مورد زمان گرفتن یک بشقاب یا نوشیدنی سردرگمی وجود ندارد.
سرویس عصر جمعه شروع میشود و همانطور که امیدوار بودید، ThreadBot تنها با همه مشتریان و وظایف همراه است، و خدمات حتی بهتر از قبل پیش میرود. شما تصور میکنید که اکنون میتوانید مشتریان بیشتری را جذب کنید، و لازم نیست نگران باشید که باید ThreadBotهای بیشتری را وارد کنید. شما به تمام پولی که قرار است پس انداز کنید فکر میکنید.
سپس، متأسفانه، مشکلی پیش میآید: یکی از وعدههای غذایی، یک سوفله پیچیده، شکست خورده است. چنین چیزی قبلاً در رستوران شما اتفاق نیفتاده است. شما شروع به مطالعه دقیق LoopBot میکنید. معلوم شد که سر یکی از میزهای شما، مهمان بسیار پرحرفی است. این مهمان به تنهایی به رستوران شما آمده است و به تلاش برای گفتگو با LoopBot ادامه میدهد، حتی گاهی اوقات LoopBot شما را با دست میگیرد. وقتی این اتفاق میافتد، LoopBot شما نمیتواند به فهرست روبهرشد وظایف در جاهای دیگر رستوران شما رسیدگی کند. به همین دلیل است که وقتی آشپزخانه اولین سوفله خود را تولید کرد: LoopBot شما نمیتوانست به آشپزخانه برگردد تا ظرف را از فر خارج کند، زیرا توسط مهمان نگه داشته شده بود.
مراسم جمعه تمام میشود و شما به خانه میروید تا در مورد آنچه آموختهاید فکر کنید. درست است که LoopBot میتوانست تمام کارهایی را که در سرویس شلوغ جمعه لازم بود انجام دهد. اما از سوی دیگر، آشپزخانه شما اولین غذای خراب خود را تولید کرد، چیزی که قبلاً هرگز اتفاق نیفتاده است. مهمانهای پرحرف همیشه WaitBotها را مشغول میکردند، اما این هرگز روی سرویس آشپزخانه تأثیری نداشت.
با در نظر گرفتن همه موارد، شما تصمیم میگیرید که همچنان بهتر است از یک LoopBot استفاده کنید. آن برخوردهای نگران کننده دیگر رخ نمیدهد، و فضای بسیار بیشتری در رستوران شما وجود دارد - فضایی که میتوانید برای مشتریان بیشتری از آن استفاده کنید. اما شما متوجه چیز عمیقی در مورد LoopBot هستید: تنها زمانی میتواند موثر باشد که هر کار کوتاه باشد، یا حداقل بتوان آن را در مدت زمان کوتاهی انجام داد. اگر هر فعالیتی LoopBot را برای مدت طولانی مشغول نگه دارد، سایر وظایف شروع به غفلت خواهند کرد.
دشوار است که از قبل بدانیم کدام کار ممکن است زمان زیادی ببرد. اگر یک مهمان کوکتلی سفارش دهد که نیاز به آمادهسازی پیچیده دارد و خیلی بیشتر از حد معمول زمان میبرد؟ اگر یک مهمان بخواهد از یک وعده غذایی در میز پذیرش شکایت کند، از پرداخت امتناع کند و بازوی LoopBot را بگیرد و از تغییر وظیفه جلوگیری کند؟ شما تصمیم میگیرید که به جای پی بردن به همه این مسائل از قبل، بهتر است با LoopBot ادامه دهید، تا حد امکان اطلاعات را ثبت کنید و بعداً با هر مشکلی، در زمانی که به وجود میآید، برخورد کنید.
زمان بیشتر میگذرد.
به تدریج، دیگر صاحبان رستوران متوجه عملکرد شما میشوند و در نهایت متوجه میشوند که آنها نیز میتوانند تنها با یک ThreadBot از پس آن برآیند و حتی پیشرفت کنند. حرف پخش میشود. به زودی هر رستوران به این شکل عمل میکند، و به یاد آوردن اینکه رستورانهای رباتیک تا به حال با چندین ThreadBot کار کردهاند، دشوار میشود.
در داستان ما، هر ربات کارگر در رستوران، یک رشته تنها (single thread) است. مشاهدات کلیدی در داستان این است که ماهیت کار در رستوران مستلزم مقدار زیادی انتظار است، همانطور که requests.get()
منتظر پاسخ از یک سرور است.
در یک رستوران، زمانی که انسانهای کند، کار دستی انجام میدهند، زمان انتظار کارگر زیاد نیست، اما وقتی روباتهای فوقکارآمد و سریع کار را انجام میدهند، تقریباً تمام وقت آنها در انتظار صرف میشود. در برنامهنویسی کامپیوتری، زمانی که برنامهنویسی شبکه در میان باشد، همین امر صادق است. CPUها کار میکنند و در I/O شبکه منتظر میمانند. پردازندههای مرکزی در رایانههای مدرن بسیار سریع هستند – صدها هزار بار سریعتر از ترافیک شبکه. بنابراین، CPUهایی که برنامههای شبکه را اجرا میکنند، زمان زیادی را صرف انتظار میکنند.
بینش در داستان این است که برنامههایی را میتوان نوشت تا به طور صریح CPU را هدایت کند تا در صورت لزوم بین وظایف کاری، حرکت کند. اگرچه یک بهبود اقتصادی به وجود آمده (استفاده از CPUهای کمتر برای یک کار)، مزیت واقعی، در مقایسه با رویکرد رشتهای (چند CPU)، حذف وضعیت رقابتی (race conditions) است.
با این حال، همه چیز گل رز نیست: همانطور که در داستان متوجه شدیم، بیشتر راه حلهای فناوری دارای مزایا و معایبی هستند. معرفی LoopBot دسته معینی از مشکلات را حل کرد، اما مشکلات جدیدی را نیز به همراه داشت – که مهمترین آن این بود که صاحب رستوران باید روشی متفاوت از برنامهنویسی را یاد میگرفت.
برای کارهای محدود به I/O، دقیقاً (فقط!) دو دلیل برای استفاده از همزمانی مبتنی بر async یا async-based concurrency، نسبت به همزمانی مبتنی بر رشته یا thread-based concurrency، وجود دارد:
-
Asyncio جایگزین ایمنتری برای چندوظیفهای پیشگیرانه (یعنی استفاده از رشتهها) ارائه میکند، در نتیجه از اشکالات، وضعیت رقابتی و سایر خطرات غیر قطعی که اغلب در برنامههای رشتهای کماهمیت رخ میدهند اجتناب میکند.
-
Asyncio یک راه ساده برای پشتیبانی از هزاران سوکت همزمان، اتصالات، از جمله توانایی مدیریت بسیاری از اتصالات طولانی مدت برای فناوریهای جدیدتر مانند WebSockets یا MQTT، اینترنت اشیا (IoT) و برنامههای کاربردی ارائه میدهد
خودشه.
Threading - به عنوان یک مدل برنامهنویسی - برای انواع خاصی از وظایف محاسباتی که به بهترین وجه با چندین CPU و حافظه مشترک برای ارتباط کارآمد بین رشتهها اجرا میشوند، مناسب است. در چنین کارهایی، استفاده از پردازش چند هستهای با حافظه مشترک یک شر ضروری است زیرا این حوزه مشکلدار به آن نیاز دارد.
برنامهنویسی شبکه یکی از این حوزهها نیست. بینش کلیدی این است که برنامهنویسی شبکه شامل مقدار زیادی "انتظار برای اتفاق افتادن چیزها" است، و به همین دلیل، ما نیازی به سیستم عامل نداریم تا وظایف خود را به طور موثر بر روی چندین CPU توزیع کند. علاوه بر این، ما به خطراتی که چندوظیفه پیشگیرانه به همراه دارد، مانند وضعیت رقابتی هنگام کار با حافظه مشترک، نیاز نداریم.
با این حال، اطلاعات نادرست زیادی در مورد دیگر مزایای فرضی مدلهای برنامهنویسی مبتنی بر رویداد وجود دارد. در اینجا چند مورد از مواردی وجود دارد که اینطور نیست:
- متاسفانه نه. در واقع، به نظر میرسد اکثر معیارها نشان میدهند که راهحلهای threading کمی سریعتر از راهحلهای متناظر آنها در Asyncio، هستند. اگر میزان همزمانی به خودی خود یک معیار عملکرد در نظر گرفته شود، Asyncio ایجاد تعداد بسیار زیادی از اتصالات سوکت همزمان را کمی آسانتر میکند. سیستمهای عامل اغلب محدودیتهایی برای ایجاد تعداد رشتهها دارند و این تعداد به طور قابل توجهی کمتر از تعداد اتصالات سوکتی است که میتوان ایجاد کرد. محدودیتهای سیستم عامل را میتوان تغییر داد، اما مطمئناً انجام آن با Asyncio آسانتر است. و در حالی که ما انتظار داریم که داشتن هزاران رشته باید هزینههای اضافی تغییر متن را به همراه داشته باشد که روتینها از آن اجتناب میکنند، به نظر میرسد که در عمل محک زدن این موضوع دشوار است. نه، سرعت، مزیت Asyncio در پایتون نیست. اگر به دنبال این هستید، به جای آن Cython را امتحان کنید!
- قطعا نه! ارزش واقعی threading در توانایی نوشتن برنامههای چند CPU است که در آن وظایف محاسباتی مختلف میتوانند حافظه را به اشتراک بگذارند. برای مثال، کتابخانه عددی numpy، از قبل با افزایش سرعت محاسبات ماتریس خاصی از طریق استفاده از چندین CPU، حتی اگر تمام حافظه مشترک است، از این روش استفاده میکند. برای عملکرد محض، هیچ رقیبی برای این مدل برنامهنویسی برای محاسبات محدود به CPU وجود ندارد.
- باز هم نه. درست است که Asyncio تحت تأثیر GIL نیست، اما این تنها به این دلیل است که GIL بر برنامه های چند رشتهای تأثیر میگذارد. "مشکلات" GIL که مردم به آن اشاره میکنند به این دلیل رخ میدهد که از موازیسازی چند هستهای واقعی هنگام استفاده از رشتهها جلوگیری میکند. از آنجایی که Asyncio تک رشتهای است (تقریباً طبق تعریف)، تحت تأثیر GIL قرار نمیگیرد، اما نمیتواند از چندین هسته CPU نیز بهرهمند شود. همچنین شایان ذکر است که در کدهای چند رشتهای، Python GIL میتواند باعث ایجاد مشکلات عملکردی بیش از حد شود. قبلاً در نکات دیگری ذکر شده است: دیو بیزلی در PyCon 2010 یک سخنرانی در این مورد به نام
"درک Python GIL"
ارائه کرد، و بسیاری از آنچه در آن سخنرانی مورد بحث قرار میگیرد، امروز همچنان صادق است.
- نادرست. امکان وضعیت رقابتی همیشه با هر برنامهنویسی همزمان وجود دارد، صرف نظر از اینکه از رشته یا برنامهنویسی مبتنی بر رویداد استفاده میشود. درست است که Asyncio عملاً میتواند کلاس خاصی از وضعیت رقابتی رایج در برنامههای چند رشتهای، مانند دسترسی به حافظه مشترک درون فرآیندی را حذف کند. با این حال، امکان انواع دیگر وضعیت رقابتی، مانند مسابقات بین فرآیندی با منابع مشترک رایج در معماریهای میکروسرویسهای توزیع شده را از بین نمیبرد. هنوز باید به نحوه استفاده از منابع مشترک توجه کنید. مزیت اصلی Asyncio نسبت به کد رشتهای این است که نقاطی که در آن کنترل اجرا بین برنامههای اصلی منتقل میشود قابل مشاهده است (به دلیل وجود کلمات کلیدی انتظار)، و بنابراین استدلال در مورد نحوه دسترسی به منابع مشترک بسیار آسانتر است.
آخرین افسانه خطرناکترین اسطوره است. پرداختن به همزمانی همیشه پیچیده است، صرف نظر از اینکه از Threading یا Asyncio استفاده میکنید. وقتی کارشناسان میگویند "Asyncio همزمانی را آسانتر میکند"، منظور آنها واقعاً این است که Asyncio جلوگیری از انواع خاصی از اشکالات وضعیت رقابتی واقعاً کابوسوار را کمی آسانتر میکند - آن نوعی که شما را در شب بیدار نگه میدارد و همزمان که صدای آتش میآید و گرگها در دوردست زوزه میکشند، در سکوت به برنامهنویسان دیگر اطلاع میدهید.
حتی با Asyncio، هنوز پیچیدگی زیادی برای مقابله با آن وجود دارد. برنامه شما چگونه از بررسیهای سلامت پشتیبانی میکند؟ چگونه میتوانید با پایگاه دادهای ارتباط برقرار کنید که ممکن است فقط چند اتصال را مجاز کند - بسیار کمتر از پنج هزار اتصال سوکت شما به مشتریان؟ هنگامی که سیگنال خاموش شدن را دریافت میکنید، چگونه برنامه شما به راحتی اتصالات را خاتمه میدهد؟ چگونه دسترسی و ورود به دیسک (مسدود کردن!) را مدیریت خواهید کرد؟ اینها تنها تعدادی از تصمیمات پیچیده طراحی هستند که باید به آنها پاسخ دهید.
طراحی برنامه همچنان دشوار خواهد بود، اما امید این است که زمانی که تنها یک رشته برای مقابله با آن دارید، زمان سادهتری برای استدلال در مورد منطق برنامه خود داشته باشید.