اجرای همزمان چندین پردازش در یک برنامه کمک بسیاری زیادی به سرعت اجرای برنامه و نیز سرعت عملکرد خواهد کرد.
در برنامه هایی که نیاز هست پردازش ها یا بخش هایی از کد به صورت همزمان اجرا شوند، از چند پردازشی یا Multi Processing استفاده میشود.
فهرست محتوای آموزش
چند پردازشی در برنامه چیست ؟
در معماری چند پردازشی، به جای استفاده از یک CPU برای انجام محاسبات و کارها، از چندین CPU به صورت موازی و همزمان استفاده میکنیم.
پیاده سازی و اجرای سیستم های چند پردازشی، مسائل و بحث های گوناگونی دارد اما به طور خیلی کلی میتوان پیاده سازی یک سیستم چند پردازشی را در دو حالت زیر در نظر گرفت.
- استفاده همزمان از چند CPU
- استفاده از یک CPU با چند هسته پردازشی (core)
هر کدام از دو مورد فوق، مزایا و معایب خود را دارند که موضوع بحث ما در این مقاله نیست.
اکثر سیستم های جدیدی که امروزه از آنها استفاده میکنیم دارای یک CPU با چند هسته هستند و شما همواره در مورد تعداد هسته های CPU دستگاه های مختلف صحبت هایی را میشنوید.
به طور قطع استفاده از چندین پردازنده به صورت همزمان، میتواند سرعت پردازش ما را افزایش دهد. بنابراین بهتر است بتوانیم برنامه هایی که در مقیاس بزرگتری اجرا میشوند را به صورت همزمانی (parallel) اجرا کرد.
ایجاد پردازش جدید در پایتون
در ابتدا فرض میکنیم تابعی با نام test()
داریم که یک متن ساده را برای ما چاپ میکند. در حالت عادی برای صدا زدن تابع به صورت زیر عمل خواهیم کرد.
def test(): print("test function ran successfully!") if __name__ == '__main__': test()
در صورتی که کد به صورت بالا اجرا شود، هم برنامه اصلی و هم تابع test()
در یک پردازنده اجرا و پردازش میشوند.
اما میخواهیم تابع test()
در پردازنده دیگری از سیستم اجرا و پردازش شود.
ایجاد process جدید
برای این کار در زبان برنامه نویسی پایتون از ماژول multiprocessing
کلاس Process
را به برنامه خود اضافه میکنیم.
from multiprocessing import Process
حال با صدا زدن Process یک شئ از این کلاس ایجاد میکنیم تا بتوانیم تابع مورد نظر را به عنوان یک پردازش جدید اجرا کنیم.
برای ایجاد شئ از نوع Process
کافی است نام تابع مورد نظر را به عنوان آرگومان target
در هنگام صدا زدن سازنده کلاس ارسال کنیم. یعنی چیزی مشابه زیر:
p = Process(target=test)
اجرای پردازش ایجاد شده
اکنون که یک شئ از نوع فرآیند (Process) داریم، با فراخوانی تابع start()
بر روی آن میتوانیم فرآیند را اجرا کنیم.
p.start()
پس از این فراخوانی، تابع test()
به صورت یک پردازش جدید و در پردازنده ای دیگر اجرا خواهد شد.
جلوگیری از پیشروی برنامه فعلی تا اتمام اجرای پردازش
پس از start شدن پردازش، برنامه فعلی به اجرای خودش ادامه خواهد داد. اگر بخواهیم برنامه فعلی تا اتمام پردازش اجرا شده منتظر بماند و ادامه آن اجرا نشود، میتوانیم از تابع join()
استفاده کنیم.
این تابع بر روی شئ فرآیند صدا زده شده و باعث میشود اجرای برنامه فعلی تا اتمام پردازش مورد نظر متوقف شود.
کد نهایی اجرای پردازش جدید به صورت زیر خواهد شد:
from multiprocessing import Process def test(): print("test function ran successfully!") if __name__ == '__main__': p = Process(target=test) p.start() p.join() # result: # test function ran successfully!
تعریف پردازش با ورودی
اگر تابعی که میخواهیم به صورت پردازش جدید اجرا شود یک یا چند ورودی از ما بخواهد، میتوان هنگام تعریف پردازش، این ورودی ها را نیز مشخص کنیم.
برای این کار در هنگام فراخوانی Process یک آرگومان args
برای آن تعریف میکنیم.
p = Process( target=test, args=( arg1, arg2, ) )
یک نمونه ساده استفاده از ورودی args
به صورت زیر می باشد.
from multiprocessing import Process def test(x): print("test function ran successfully! and x = " + str(x)) if __name__ == '__main__': p = Process(target=test, args=(10,)) p.start() p.join() # result: # test function ran successfully! and x = 10
پردازش جدید در پردازنده چگونه ایجاد میشود ؟
اگر سیستم ما چندین CPU مجزا از هم داشته باشد، به ازای هر CPU منابع مخصوصی خواهیم داشت و سیستم عامل و در برخی معماری یک پردازنده اصلی به نام Master وظیفه تخصیص وظایف را به هر پردازنده بر عهده دارند.
در صورتی که یک CPU با چند هسته پردازشی داشته باشیم، در اکثر موارد، برخی منابع میان تمام هسته ها اشتراکی خواهد بود، مثل گذرگاه داده (Data Bus).
همانطور که میدانید هر پردازش در سیستم عامل دارای یک id جهت شناسایی است. برای ببینیم آیا با ایجاد یک Process جدید، واقعا یک پردازش با pid جدید ایجاد شده یا نه، میتوانیم مقدار pid را در برنامه اصلی و نیز تابع test()
مورد بررسی قرار دهیم.
برای به دست آوردن pid فرآیندی که در حال پردازش برنامه فعلی است میتوان از ماژول os
کمک گرفت.
import os
در ماژول os تابعی با نام getpid()
وجود دارد که مقدار pid پردازش فعلی را به ما باز میگرداند.
os.getpid()
با جایگذاری تابع فوق در بخش های مناسبی از کد، کدی مشابه زیر خواهیم داشت.
from multiprocessing import Process import os def test(): print("process ID is: " + str( os.getpid() )) if __name__ == '__main__': print("main process ID is: " + str( os.getpid() )) p = Process(target=test) p.start() p.join()
با اجرای این برنامه نتیجه ای مشابه زیر به دست خواهیم آورد. دقت داشته باشید که pid برنامه در هر سیستم و در هر زمانی متفاوت است.
# result: # main process ID is: 12924 # process ID is: 14100
اجرای چند برنامه همزمان
مزیت استفاده از چند پردازشی در پایتون یا هر زبان دیگر (Multi Processing) در برنامه ها، اجرای چند فرآیند به صورت همزمان است.
ما میتوانیم در برنامه خود چندین پردازش ایجاد کرده و همه آنها را با یکدیگر اجرا کنیم و به پردازنده ها اجازه دهیم به صورت همزمان در کنار هم کار کنند.
برای آنکه بهتر متوجه اجرای همزمان برنامه ها شوید، یک مثال ساده و ابتدایی را در ادامه بررسی خواهیم کرد.
فرض فرض کنید تابعی به نام test2()
داریم که یک رشته را به عنوان تنها ورودی خود دریافت کرده و آنرا به تعداد 6 بار در خروجی چاپ میکند.
میخواهیم این تابع را با دو مقدار ورودی متفاوت و در دو پردازش مجزا اجرا کرده و نتیجه آنرا ببینیم.
کد برنامه ما برای چنین مسئله ای به صورت زیر خواهد بود.
from multiprocessing import Process def test2(x): for i in range(6): print(x) if __name__ == '__main__': p1 = Process(target=test2, args=("p1",)) p2 = Process(target=test2, args=("p2",)) p1.start() p2.start() p1.join() p2.join()
با اجرای این برنامه یکی از خروجی های ممکن به صورت زیر میشود.
# result: # p2 # p1 # p2 # p1 # p1 # p2 # p1 # p2 # p1 # p2 # p1 # p2
همانطور که میبینید دو پردازش ما در لابه لای هم به صورت همزمان در حال اجرا هستند.
البته توجه داشته باشید که به دلیل ساده بودن و سریع بودن برنامه، ممکن است اجرای اولین پردازش زمان زیادی به طور نیانجامد.
بنابراین ممکن است ابتدا تمام 6 مورد p1 و سپس شش مورد p2 چاپ شوند.
اما باز هم خللی در ساختار و فرآیندی که گفته شد به وجود نخواهد آمد.
جمع بندی برنامه نویسی چند پردازشی در پایتون
در این مقاله سعی شد به صورت کاملاً ساده و ابتدایی مبحث چند پردازشی در پایتون و نحوه پیاده سازی آن با یک مثال ساده را بررسی کنیم.
چند پردازشی جزئیات زیادی دارد؛ برای مثال در صورتی که بخواهیم پردازش های ایجاد شده ما به یک داده مشترک دسترسی داشته باشند.
در این صورت مشکلاتی نظیر همزمانی و تغییر داده مشترک وجود خواهد داشت که میتوان با روش های خاصی آنها را مدیریت کرد.
این آموزش برای همیشه رایگانه! میتونید با اشتراکگذاری لینک این صفحه از ما حمایت کنید یا با خرید یه فنجون نوشیدنی بهمون انرژی بدید!
میخوام یه نوشیدنی مهمونتون کنم
ممنونم جالب بود
خوشحالیم که براتون مفید و جذاب بوده مهدی جان
سلام ،
من یه برنامه پایتونی دارم
که چند تا تابع داره
چطور میتونم همزمان این توابع رو اجرا کنم
سلام
شما میتونید چند تا Process که هر کدوم حاوی یکی از توابعتون هستن رو ایجاد و مشابه آموزش اجراشون کنید.
سلام
من دارم یه برنامه میسازم که دیتای لایو قیمت رو از یه سایتی میگیره و میخوام چارت رو به صورت آنلاین رسم کنم. همین الان هم این کار رو کردم اما روش ام این بوده که اول میام دیتا رو توی یه فایلی میریزم و بعد توی یه کد دیگه ای میام و از دیتای اون فایل برای رسم دیتا ام استفاده می کنم.
اما میخواستم بدونم که میشه همین کار رو بدون اینکه دو تا کد پیاتون رو جداگانه اجرا بگیرم انجام داد؟ با همین روش های چند پردازشی؟
سلام
قراره این ۲ تا کار (گرفتن دیتا + نمایش نمودار) دورهای انجام بشه؟ مثلا هر n دقیقه؟ آیا حتماً نیازه در لحظه دیتا رو بگیرید و همون لحظه هم چارت رو ایجاد کنید؟
اگه نه، چرا این کارها رو پشتِ سرِ هم انجام نمیدیدین؟ یعنی دیتا رو بگیره و بعدم نمودار بکشه. حالا میتونه دیتا رو در بازههای n دقیقهای بگیره ولی نمودار رو m دقیقهای!
از چند پردازشی زمانی استفاده میکنیم که بخوایم «تقریباً در یک لحظه» چند کار رو انجام بدیم؛ احتمالاً برای شما ضرورتی به این کار نباشه.
راستش هدف از این کار نشون دادن نمودار لحظه ای قیمت بازار های سهام به صورت نمودار های تکنیکال هست که دیتا به صورت لحظه ای دیده میشه. یعنی میخوام این دیتا رو که از سورس می گیرم همزمان قیمت ها روی نمودار نمایش داده بشن و تغییر کنند، برای همین هم نیاز به همزمانی دارم.
اینطوری که توضیح دادین فرض میکنیم شما دو تا حلقه بینهایت داری که این دو تا کار رو انجام میده. میتونید یکی از این حلقهها رو داخل به process دیگه تعریف و اجراش کنید.
یعنی برنامهای که اجرا میشه پروسس رو اجرا کنه و بره داخل یه حلقه از کارها. اون پروسس هم حلقه دومی رو اجرا کنه. یکی از معمولترین حالتها همینه.
نوشیدنی هاتون رو از کجا میخرید؟
چطور؟ 😉
خیلی ارزون هستن… مرد حسابی الان با 5 هزار تومن میشه چایی خرید؟
فعلاً ما چای کیسهای میزنیم؛ آبجوشم با خودمون! 😉
سلام . ممنون بابت توضیحات خوبتون
من با کتابخونه ی tk کار میکنم . یک ماشین حساب درست کردم که میگه عدد اول رو توش وارد می کنیم همینطور عدد دوم و نشانه ی عملیات رو . بعد وقتی جواب رو می بینیم میپرسه که آیا می خواهید یک بار دیگه انجام بدید و دو تا checkbutton به نام yes و no درست کردم . بعدش هم کلیدی به نام تایید درست کردم . باید چی کار کنم که وقتی کلید تایید رو زدم دوباره توی پنجره ی جدید همون کارهای قبلی رو انجام بده . از روشی که شما عرض کرده بودید استفاده کردم . وقتی که yes رو می زدم اجرا می کرد اما وقتی توی اون صفحه ی دومی که ایجاد کرده بودم کلید تایید کار نمی کرد باید صفحه دوم رو می بستم و دوباره از اونجا کلید تایید رو بزنم . باید چی کار کنم که توی صفحه دوم هم کلید تایید کار کنه.
سلام
برای این کاری که توضیح دادین به نظر اجبار یا نیازی به استفاده از چند پردازشی نیست. چون شما در هر لحظه نیازه یک کار رو انجام بدید؛ مثلاً محاسبه کنید، پیام نشون بدید یا محتوای window رو تغییر بدید.
پیشنهاد اینکه موقتاً قسمت چند پردازشی رو بردارید و سعی کنید کدتون به صورت عادی کار کنه. اگه اوکی بود و لازم داشتید، processها رو به کدِ اصلاحشده اضافه کنید.
سلام
ما برنامه ای میخواهیم که هم یک ورودی از کاربر بگیره و هم زمان رو حساب کنه اگر خیلی زمان زیاد شد برنامه خارج بشه
این رو چجوری میشه اجرا کرد
سلام
میتونید دو تا process با پایتون ایجاد کنید. یکیش منتظر ورودی بمونه و دیگری مثلاً با کتابخونه time که آموزشش توی سایت هست زمان رو محاسبه کنه. بعد از زمان موردنظر میتوین پردازش اولی رو kill کنید یا مثلاً پیامی نمایش بدید و … .
سلام دستور های دیگه ای وجود داره به غیر mainloop که بتونه با اجرای پنجره بصورت یک حلقه کار نکنه و هی کد ها از اول تکرار نشن؟
سلام
منظورتون از mainloop در چندپردازشی چیه؟ اگر تابعی هست که به عنوان process اجرا میشه، شما میتونید هر پردازشی غیر از حلقه هم در اون اجرا کنید؛ مشابه مثال.
سلام اگر که بخوام با یک لیبل در یک پنجره یک تایمر را نمایش بدم و در کنارش در همان پنجره به صورت همزمان یک تایمر دیگه هم نمایش داده بشه به همین اموزش ارتباط پیدا میکنه؟یکم گیج شدم نمیدونم برای این کار بصورت معمولی میشه انجام بشه یا باید از دستورات چند پردازشی استفاده بشه؟
سلام
اگر از tkinter استفاده میکنید احتمال زیاد متد
after()
که روی شئ TK صدا زده میشه کارتون رو راه بندازه.مثلاً در انتهای تابع نمایش ساعت،
root.after(1000, clock)
رو صدا میزنید. که یعنی بعد از 1 ثانیه (1000 میلی ثانیه) مجدداً تابعclock()
رو اجرا کنه.البته استفاده از چندپردازشی و اجرای چند تابع آپدیت تایمر بهطور همزمان هم صحیحه.
واقعا ممنونم خیلی لطف کردی واقعا
این اموزش کمکم کرد
سلام مجدد فقط اینکه برداشتم از صحبتت این سورس را برای مثال نوشتم
الان اگر اجرا میشه دوباره همون clock نمایش داده میشه و خب یک سری عدد نوشته میشه و بروزم نمیشه میشه اشتباهم را بگی کجا بوده
مقدار لیبل رو یادتون رفته توی تابع آپدیت کنین. لیبل رو قبل از تابع تعریف کنید و برای آپدیت متن هم شبیه زیر عمل کنید:
پیشنهاد میکنم
import time
رو ابتدای برنامه بذارید و زمان رو هم به string تبدیل کنید.سلام عزیز من هم مشکل دوستمون را داشتم از دستور افتری هم که گفتی استفاده کردم الان بعد از ۱ ثانیه تابع را اجرا میکنن و تابع همونجوری میمونه نه بروز میشه نه بسته
سلام
در بدنه تابعی که هر ۱ ثانیه اجرا میشه مقدار خروجی رو آپدیت میکنید؟
آموزش مفیدی بود، ممنون
سلام، ممنون خیلی خوب توضیح داده بودین،
فقط یک سوال، امکان پردازش چند هسته ای برای GPU هم هست؟ اگر بخوایم با Keras یا Tensorflow کار بکنیم …
سلام
تجربه زیادی در پردازش با GPU ندارم. احتمال میدم امکانش باشه. چون پردازشهای موازی و چند پردازشی روی GPU برای محاسبههای بزرگ نیازه.
سلام. آموزش خیلی عالی بود.
فقط یک سوال!
تا چند پردازش رو میشه همزمان انجام داد؟
سلام
وقتی چند پردازشی یا چند نخی داریم، مدیریت این پردازشها بر عهده سیستم عامل هست. در حالت معمول، شما هیچ محدودیتی در تعداد پردازش همزمان با پایتون ندارید. همین الآن سیستم عامل شما تعداد زیادی پردازش رو همزمان داره اجرا میکنه.
البته اینها واقعاً همزمان نیستند! بلکه نوبتی و در هر لحظه فقط به تعداد هسته CPU شما اجرا میشن. برای اینکه با نحوه مدیریت و اجراشون آشنا بشید، میتونید آموزش الگوریتمهای زمانبندی پردازنده رو ببینید.
سلام واقعا عالی بود چه توضیح چه مثال هاتون که خیلی قشنگ مفهوم وکاربرد و به خواننده منتقل کرد خدا قوت
خوشحالم براتون مفید بوده. ممنون بابت انرژی خوبی که به ما میدید.
سلام
واقعا ممنونم بابت اموزش های صریح و روانتون…
کاش این قسمت پایتون رو ادامه می دادید.چون واقعا خیلی خوب به زبان ساده مفهوم رو رسوندید
انشالله موفق باشید همیشه
سلام
ممنون بابت انرژی دادنِ فوق العادتون
خیلی خوشحالم که این آموزشها مفید هستند…
حتماً، آموزشهای جدیدی برای پایتون در نظر داریم؛ طی هفتههای آینده حتما سایت رو بررسی کنین 😉
موفق باشید