PDA

View Full Version : آموزش: Thread in Python



Bahmany
سه شنبه 07 آبان 1392, 10:45 صبح
سلام
تعدادی سوال برام پیام زده بودن دوستان درباره ی Thread در پایتون :
تو این پست یحث مقدماتی رو اشاره میکنم و سپس همیجوری تو زمانایی که برام آزاد بشه جلو تر میرم :
البته این بحث رو بنده از سایت پایتون براتون مینویسم
کار با Thread در پایتون مثل بیشتر زبان های برنامه نویسی هست ولی از اونها ساده تر !!!
· Threadهای متفاوت در یک پروسس در فضای Thread اصلی (منظور تردی هست که مربوط به کد های اجرایی شما ) اجرا میشوند ، این امر موجب به اشتراک گذاری بهتر و ارتباط بهتر Thread ها در پایتون میشود
· Threadها بعضی مواقع فرایندهای سبک وزن نامیده میشوند به این علت که فرایندهای سربار حافظه نیستند و نیاز به حافظه زیاد ندارند و همچنین در فرایندهای حجیم برای ما به صرفه تر هستند
یک ترد ( منظورم همون Thread هست << البته چی توز هم میشه ها ،،،،،، تررررد !! ) در پایتون و صد البته در بیشتر زبان ها ترد ها یک شروع ، فرایند اجرا و نتیجه را همراه خود دارند، برای اینکه بفهمیم این ترد در چه فرایندی ( اجرا ، شروع یا پایان ) هست پایتون Tracker های جالبی داره

در زیر اصلی ترین و مقدماتی ترین کد اجرایی پایتون رو مشاهده میکنید


thread.start_new_thread ( function, args[, kwargs] )


در مثال زیر نحوه استفاده از دستور اجرایی ترد را ببینید :

مثال :








#!/usr/bin/python

import thread
import time

# Define a function for the thread
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print "%s: %s" % ( threadName, time.ctime(time.time()) )

# Create two threads as follows
try:
thread.start_new_thread( print_time, ("Thread-1", 2, ) )
thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
print "Error: unable to start thread"

while 1:
pass




و خروجی






Thread-1: Thu Jan 22 15:42:17 2009



Thread-1: Thu Jan 22 15:42:19 2009



Thread-2: Thu Jan 22 15:42:19 2009



Thread-1: Thu Jan 22 15:42:21 2009



Thread-2: Thu Jan 22 15:42:23 2009



Thread-1: Thu Jan 22 15:42:23 2009



Thread-1: Thu Jan 22 15:42:25 2009



Thread-2: Thu Jan 22 15:42:27 2009



Thread-2: Thu Jan 22 15:42:31 2009



Thread-2: Thu Jan 22 15:42:35 2009





تو پست بعدی نگاهی به پشت پرده ی کدهای اشاره شده ی بالا میپردازم

Bahmany
شنبه 11 آبان 1392, 07:51 صبح
معماری تردها در پایتون ( ترد = Thread;) )
مقدمه
همونطور که قبلا اشاره کردم پایتون زبونی ساده برای کار با تردهاست اما در نظر گرفتن نکاتی بسیار ضروریست ، لابلای این نکات به معماری تردها در پایتون نیز اشاره خواهد شد:
مهمترین مسئله ی استفاده کنندگان از ترد در پایتون ، مسئله ی Global Interpreter Lock ( به ترجمه ی بنده محدودیت کلی مترجم ( این بحث مترجم و کمپایلر در پایتون کلی بحث داره که خوبه بعدا بهش پرداخته بشه ) ) است. این مسئله فقط مجوز دسترسی یک ترد به مترجم را فراهم میکند. این مسئله به دو نکته در خود دارد : 1- معمولا به ندرت نیاز به استفاده از دستورات مربوط به محدودیت اشاره شده در ( Global Interpreter lock ) پایتون میباشد ( این مسئله نیاز به روشن شدن بیشتر دارد که در ادامه توضیح داده خواهد شد ) 2- جهت استفاده از مزایای استفاده از سیستم های چندپردازشی شما میبایست روندهای جداگانه استفاده کنید.برنامه نویس ابتدا میبایست هدف خود را در استفاده از ترد مشخص نماید. آیا هدف برنامه نویس استفاده از مزایای معماری چند هسته ای میباشد ؟ که در این صورت میبایست روندهای نرم افزار در قالبت چند روندی یا Multi Process طراحی و پیاده سازی شود. در استفاده از مزایای چند هسته ای ، روندها در پردازش های مختلف به هسته های اولویت بندی شده بوسیله ماشین مجازی پایتون و سپس بوسیله ی سیستم عامل محاسبه شده و نتیجه از همین مسیر به نرم افزار بر میگردد.
در ادامه به شرح طراحی ترد در پایتون میپردازم :

CPython ( اشاره مختصر و سریع )
جهت ادامه ی بحث مدیریت ترد ها لازم است یک سری از بسته های پایتون رو معرفی نمود.
سی پایتون از پایتون در محیط سی برگرفته شده است. این کاره ها ( برنامه نویس ها ;) ) به جهت جدا سازی این بسته از پایتون آنرا سی پایتون میخوانند. سی پایتون رخدادیست که کد کدهای شما رو بصورت کد بایت های C به اجرا می آورد. این کدها به کدبایت های سی درآورده شده و آنها را در چرخه ی نظارتی خود ترجمه میکند. پس CPython یک مترجم کدبایتی است و در نتیجه سی پایتون را میتوان بعنوان یک مترجمی معرفی نمود که مکانیزم نوشتن به زبان پایتون و کمپایل به زبون سی معرفی کرد. (به این مقوله ( نوشتن به زبانی و کمپایل به زبونی دیگر ) Foreign Function interface نیز گفته میشود. )
حالا پس از خواندن پاراگرف بالا به بسته های زیر نیز توجه بشه بد نیست :
Jython نیز کد ها را در بایت کد های جاوایی کمپایل میکند و بشما اجازه میدهد کدهای خود را در JVM نیز به اجرا درآورید
IronPython نیز بشما اجازه میدهد کدهای خود را در Microsoft CLR اجرا نمایید.
و PyPy محیطی را فراهم میکند تا کدهای شما سریعتر از سی پاتون با همان ساختار سی پایتون اجرا شوند.
پس فهمیدیم که پایتون بسته هایی دارد که بشما اجازه میدهد کد های خود را تحت زبان های مختلف کمپایل و اجرا نمایید.

GIL یا همون Global Interpreter Lock
در سی پایتون ، GIL یک mutex است که بر تردهای مختلف اجرا کننده ی کدبایت های پایتون محدودیت هایی را اعمال می کند.
(موتکس : موتکس به ابزارهایی جهت اطمینان از اینکه هیچ دو پردازشی یا دو تردی در Critical Section بصورت همزمان اجرا نخواهند شد گفته میشود. Critical Section نیز به زمانی گفته میشود که یک روند در حال استفاده یا بررسی منابع اشتراکی میباشد ( منابع اشتراکی مثل حافظه ی به اشتراک گذاشته شده Shared Memory))
MUTEX >> wiki (http://en.wikipedia.org/wiki/Mutual_exclusion)
http://upload.wikimedia.org/wikipedia/commons/2/2f/Mutual_exclusion_example_with_linked_list.png
این محدودیت اعمال شده جزو ضرویات پایه ای سی پایتون است چون سیستم مدیریت حافظه سی پایتون thread-safe نیست و روندها رو بصورت تک پردازشی انجام میشود. پس نتیجه میگیریم مترجم پایتون یه مترجم چندپردازشی نیست ، و برای پشتیبانی از پردازش های multithread از محیط همه جانبه ای بنام GIL که در ابتدای کار میبایست بوسیله ی ترد اصلی پایتون ایجاد شده تا امکان دسترسی به آبکت های پایتون را فراهم آورد استفاده میکند.
بدون دخالت GIL حتی ساده ترین روند ها نیز میتواند در مولتی تردینگ مسئله ساز شود.

در پست بعدی GIL بازتر میشود

Bahmany
شنبه 11 آبان 1392, 10:56 صبح
GIL :
GIL معمولا بحث برانگیز است ، به این علت که این سیستم مولتی تردهای سی پایتون را از مزایای مولتی پراسس منع می کند. ( بطور بلقوه روندهای طولانی مانند مدیریت های خروجی و ورودی یا Image Processing خارج از GIL کار میشوند. بنا بر این در برنامه های مولتی تردینگی CPython که پروسس های طولانی مدت دارند GIL را به تگنا می رسانند.
سوال : پس GIL برای مولتی تردینگ در سیستم های مولتی کور چه راهکاری دارد ؟؟ GIL رفتاری جالب و منحصربفردی در این حوزه دارد که نظر توسعه دهندگان را بخود جلب میکند J
بیایید باهم ببینیم :
این تابع به درد نخور را در نظر بگیرید :

def count(n):
while n > 0:
n -= 1

حالا این تابع را دوبار پیاپی اجرا نمایید :



count(100000000)
count(100000000)

و حالا این دو را در دو ترد بصورت پارالل اجرا کنید :
فول سورس :



from Queue import Queue
from threading import Thread
import datetime
def count(n):
print datetime.datetime.time(datetime.datetime.now())
while n > 0:
n -= 1
print datetime.datetime.time(datetime.datetime.now())
count(100000)
count(100000)
print "--------------"
print "In Thread"
t1 = Thread(target=count,args=(100000,))
t1.run()
t2 = Thread(target=count,args=(100000,))
t2.run()

و اما خروجی




11:11:41.488000
11:11:41.662000
11:11:41.662000
11:11:41.834000
--------------
In Thread
11:11:41.834000
11:11:42.024000
11:11:42.024000
11:11:42.213000

و در آخر نتیجه :
آخر چرا در یک سیستم 4 کور با 8 گیگ رم ترد ها با سرعت تقریبی 1.5x آهسته تر اجرا شدند ؟؟
اگه حالا برنامه رو با یک کور اجرا کنم چه اتفاقی می افتد ؟؟ بللله !!! 1.3x آهسته تر !!
حالا کمی روی این موضوع فکر کنیم J
.
.
.
.
.
. ( مثلا در حال فکر کردن هستیم ) !! @
.
.
.
.
.
.
و اما رفتار GIL
· این مسئله ساده هست : ترد ها GIL را در حین اجرا از آن خود میکنند

112470

· در بالا مدلی از مولتی تردینگ که بصورت هماهنگ کار میکنند نمایش داده شده است




nv vدر پست بعدی باز هم GIL GIL GIL GIL :))

eshpilen
پنج شنبه 16 آبان 1392, 09:43 صبح
ای بابا یعنی تردهای پایتون از چند پردازنده/هسته بصورت بهینه استفاده نمیکنن؟

Bahmany
یک شنبه 19 آبان 1392, 12:31 عصر
استفاده میکنن ولی با مدل و اصول خودشون
تو متن بعدی این مسئله بهمراه معماریش مشخص میشه
روزهای پرمشغله ای دارم ، به محض اینکه تموم بشه این بحث جالب رو تموم میکنیم

hamedlll
سه شنبه 21 آبان 1392, 12:14 عصر
برای این کار باید از کتابخونه multiprocessing استفاده کرد کلا الان دیگه استفاده از threading اشتباه است.
ای بابا یعنی تردهای پایتون از چند پردازنده/هسته بصورت بهینه استفاده نمیکنن؟

eshpilen
جمعه 24 آبان 1392, 18:29 عصر
استفاده میکنن ولی با مدل و اصول خودشون
تو متن بعدی این مسئله بهمراه معماریش مشخص میشه
روزهای پرمشغله ای دارم ، به محض اینکه تموم بشه این بحث جالب رو تموم میکنیم
خود مطالب حاکی از این هستن.
درواقع پایتون ترد واقعی کار نمیکنه.
ترد هست، ولی چون همه توسط یک مفسر اجرا میشن و این مفسر بطور کامل از قابلیت مالتی ترد استفاده نمیکنه، استفادهء بهینه از سخت افزار نمیشه. یعنی درواقع یک مفسر هست که مدام بین چند ترد باید سویچ کنه.
اون تصویر هم که گذاشتی نشون میده فقط موقعی که یک ترد درگیر عملیات IO هست (که در نتیجه به محیط داخلی مفسر پایتون کاری نداره)، GIL آزاد میشه و در نتیجه تردهای دیگر میتونن بصورت بهینه تری اجرا بشن.
البته این چیزی بود که بنده برداشت کردم.

eshpilen
جمعه 24 آبان 1392, 18:32 عصر
برای این کار باید از کتابخونه multiprocessing استفاده کرد کلا الان دیگه استفاده از threading اشتباه است.
بهرحال خواص ترد با پراسس تفاوت میکنه.
ضمنا سیستم عاملها هم در زمینهء سرعت و هزینهء ایجاد پراسس ها و تردها تفاوت دارن. مثلا در ویندوز ایجاد یک پراسس جدید عملیات سنگین و زمانبری هست، ولی در لینوکس بعکس ایجاد پراسس یک عملیات سبک و سریعه (و بعکس، ایجاد ترد در لینوکس یک مقدار کند و سنگین است).

pswin.pooya
یک شنبه 24 آبان 1394, 12:51 عصر
و بعکس، ایجاد ترد در لینوکس یک مقدار کند و سنگین است.
???!!!!!
برام سوال شد که دلیلش چیه؟!



دوستان یه موردی که باید خیلی دقت کنید بهتش چند پردازنده ای با چند هسته بودن و مولتی ترد بودن کلا فرق می کنه. و این مفهوم ها کاملا متفاوت هستند. تقریبا تنها زبونی که ساپورت کامل از این روشها می ده C++‎‎‎ هست و من زبان دیگه ای رو ندیدم که شما بتونید داخل اون تمامی این مفاهیم رو کنترل کنید.

اول از همه قبلا از اینکه بخواییم مولتی ترد. یا چند هسته و ... کار کنید باید دقت کنید که هدف شما چیست.

اگر سرعت محاسبات هست که معمولا میشه با SIMD (داخل پردازدنده های x86 سری دستورات XMM و SSEبهش برسید.) و همچنین میشه داخل C از OpenMP استفاده کرد که تردهای سبکی رو ایجاد می کنه. (البته الگوریتمون باید قابلیت موازی شدن رو داشته باشه.)

اگر نوع داده شما بلوکی هست مثلا پردازش تصویر و ... پردازنده های با ساختار MIMD و یا آرایه ای و ... مثل پردازنده کارت گرافیک سرعت بیشتری رو می تونن ارائه بکنن. که داخل C++‎‎‎ میشه از روشهای مختلف از جمله OpenCL این کار رو کرد.

اگر هدف شما جلوگیری از بلوک شدن برنامه برای IO باشه که همون تردهای سیستم عامل کار رو انجام میدن. و نیازی به کنترل چند پردازندگی و چند هسته ای و ... نیست.

آیا الزاما چند هسته و مولتی ترد بودن سرعت رو بالا می بره؟ جواب در یک کلمه نه. باید موازنه خیلی دقیقی وجود داشته باشه که محاسبات پیچیده ای رو داره. بهترین حالت تست و خطا هست. در مورد سرعت هنوز هیچ تعریفی وجود نداره و الارغم تحقیق های زیاد حتی حرفه ای ترینها هم نمی تونن سرعت رو در حالت کلی (مثلا سرعت دو پردازنده کاملا متفاوت رو) رو باهم بسنجن. مثلا برای همین داده های بلوکی یه پردازنده گرافیکی ۹۰۰ مگارهرتز شاید از یه پردازنده ۳.۶ گیگاهرتز اینتل سریعتر کار کنه