آموزش پایتون - برنامهنویسی چندرشتهای
اجرای چند رشته مشابه اجرای چند برنامه مختلف بهطور همزمان است، اما با این مزایا:
-
چندین رشته در یک فرایند فضای داده مشترکی با رشته اصلی دارند و بنابراین میتوانند اطلاعات را با یکدیگر بهراحتی به اشتراک بگذارند یا با یکدیگر ارتباط برقرار کنند، بیشتر از آنکه فرایندهای جداگانه باشند.
-
رشتهها گاهی اوقات فرایندهای سبکوزن نامیده میشوند و نیازی به حافظه اضافی زیادی ندارند؛ آنها ارزانتر از فرایندها هستند.
یک رشته دارای یک آغاز، یک دنباله اجرا، و یک نتیجه است. آن یک نشانگر دستورالعمل دارد که ردیابی میکند که در کجای زمینه خود در حال اجرا است.
-
میتواند قطع شود (متوقف شود)
-
میتواند بهطور موقت در حالت تعلیق قرار گیرد (که به آن خوابیدن نیز گفته میشود) در حالی که رشتههای دیگر در حال اجرا هستند - این کار yielding نامیده میشود.
شروع یک رشته جدید
برای ایجاد یک رشته جدید، باید روش زیر را که در ماژول thread موجود است، فراخوانی کنید:
thread.start_new_thread ( function, args[, kwargs] )
این روش تماس، یک راه سریع و کارآمد برای ایجاد رشتههای جدید در هر دو سیستم عامل لینوکس و ویندوز را فراهم میکند.
روش تماس بلافاصله برمیگردد و رشته فرزند شروع میشود و تابع را با لیست args ارسال شده فراخوانی میکند. زمانی که تابع برمیگردد، رشته خاتمه مییابد.
در اینجا، args یک Tuple از آرگومانها است؛ برای فراخوانی تابع بدون ارسال هیچ آرگومانی، از یک Tuple خالی استفاده کنید. 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
اگرچه برای چندرشتهای سطح پایین بسیار موثر است، اما ماژول thread در مقایسه با ماژول threading جدیدتر بسیار محدود است.
ماژول Threading
ماژول threading جدیدتر که در Python 2.4 گنجانده شده است، پشتیبانی بسیار قدرتمندتر و سطح بالاتری از رشتهها را نسبت به ماژول thread که در بخش قبلی بحث شد، ارائه میدهد.
ماژول threading همه روشهای ماژول thread را در معرض دید قرار میدهد و برخی روشهای اضافی را نیز ارائه میدهد:
-
threading.activeCount() − تعداد اشیاء رشتهای که فعال هستند را برمیگرداند.
-
threading.currentThread() − تعداد اشیاء رشتهای را در کنترل رشتهی فراخوان برمیگرداند.
-
threading.enumerate() − یک لیست از همه اشیاء رشتهای که در حال حاضر فعال هستند را برمیگرداند.
علاوه بر روشها، ماژول threading کلاس Thread را دارد که threading را پیادهسازی میکند. روشهای ارائه شده توسط کلاس Thread به شرح زیر است:
-
run() − روش run() نقطه ورودی برای یک رشته است.
-
start() − روش start() یک رشته را با فراخوانی روش run شروع میکند.
-
join([time]) − روش join() منتظر خاتمه رشتهها میماند.
-
isAlive() − روش isAlive() بررسی میکند که آیا یک رشته هنوز در حال اجرا است.
-
getName() − روش getName() نام یک رشته را برمیگرداند.
-
setName() − روش setName() نام یک رشته را تعیین میکند.
ایجاد رشته با استفاده از ماژول Threading
برای پیادهسازی یک رشته جدید با استفاده از ماژول threading، باید موارد زیر را انجام دهید:
-
یک زیر کلاس جدید از کلاس Thread تعریف کنید.
-
متد __init__(self [,args]) را برای افزودن آرگومانهای اضافی override کنید.
-
سپس، متد run(self [,args]) را override کنید تا پیادهسازی کنید که رشته چه کاری باید انجام دهد وقتی شروع میشود.
هنگامی که زیر کلاس جدید Thread را ایجاد کردید، میتوانید یک instance از آن ایجاد کنید و سپس با فراخوانی start()، که به نوبه خود متد run() را فراخوانی میکند، یک رشته جدید را شروع کنید.
مثال
#!/usr/bin/python
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print "Starting " + self.name
print_time(self.name, 5, self.counter)
print "Exiting " + self.name
def print_time(threadName, counter, delay):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print "%s: %s" % (threadName, time.ctime(time.time()))
counter -= 1
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
print "Exiting Main Thread"
هنگامی که کد بالا اجرا میشود، نتیجه زیر را تولید میکند:
Starting Thread-1
Starting Thread-2
Exiting Main Thread
Thread-1: Thu Mar 21 09:10:03 2013
Thread-1: Thu Mar 21 09:10:04 2013
Thread-2: Thu Mar 21 09:10:04 2013
Thread-1: Thu Mar 21 09:10:05 2013
Thread-1: Thu Mar 21 09:10:06 2013
Thread-2: Thu Mar 21 09:10:06 2013
Thread-1: Thu Mar 21 09:10:07 2013
Exiting Thread-1
Thread-2: Thu Mar 21 09:10:08 2013
Thread-2: Thu Mar 21 09:10:10 2013
Thread-2: Thu Mar 21 09:10:12 2013
Exiting Thread-2
همگامسازی رشتهها
ماژول threading که همراه با Python ارائه میشود، یک مکانیزم قفلگذاری ساده برای پیادهسازی را فراهم میکند که به شما امکان میدهد رشتهها را همگامسازی کنید. یک قفل جدید با فراخوانی متد Lock() ایجاد میشود، که قفل جدید را برمیگرداند.
متد acquire(blocking) شیء قفل جدید برای مجبور کردن رشتهها به اجرا به صورت همگام استفاده میشود. پارامتر اختیاری blocking به شما امکان میدهد کنترل کنید که آیا رشته منتظر acquire قفل است.
اگر blocking 0 باشد، رشته بلافاصله با مقدار 0 اگر قفل قابل acquire نیست و با مقدار 1 اگر قفل acquire شده است، برمیگردد. اگر blocking 1 باشد، رشته مسدود میشود و منتظر آزاد شدن قفل میماند.
متد release() شیء قفل جدید برای آزاد کردن قفل زمانی که دیگر لازم نیست استفاده میشود.
مثال
#!/usr/bin/python
import threading
import time
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print "Starting " + self.name
# Get lock to synchronize threads
threadLock.acquire()
print_time(self.name, self.counter, 3)
# Free lock to release next thread
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print "%s: %s" % (threadName, time.ctime(time.time()))
counter -= 1
threadLock = threading.Lock()
threads = []
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
# Add threads to thread list
threads.append(thread1)
threads.append(thread2)
# Wait for all threads to complete
for t in threads:
t.join()
print "Exiting Main Thread"
هنگامی که کد بالا اجرا میشود، نتیجه زیر را تولید میکند:
Starting Thread-1
Starting Thread-2
Thread-1: Thu Mar 21 09:11:28 2013
Thread-1: Thu Mar 21 09:11:29 2013
Thread-1: Thu Mar 21 09:11:30 2013
Thread-2: Thu Mar 21 09:11:32 2013
Thread-2: Thu Mar 21 09:11:34 2013
Thread-2: Thu Mar 21 09:11:36 2013
Exiting Main Thread
صف اولویت چندرشتهای
ماژول Queue به شما امکان میدهد یک شیء صف جدید ایجاد کنید که میتواند تعداد مشخصی از عناصر را در خود نگه دارد. روشهای زیر برای کنترل صف وجود دارد:
-
get() − متد get() یک عنصر را از صف حذف میکند و آن را برمیگرداند.
-
put() − متد put یک عنصر را به صف اضافه میکند.
-
qsize() − متد qsize() تعداد عناصری را که در حال حاضر در صف هستند، برمیگرداند.
-
empty() − متد empty() اگر صف خالی است، True را برمیگرداند؛ در غیر این صورت، False.
-
full() − متد full() اگر صف پر است، True را برمیگرداند؛ در غیر این صورت، False.
مثال
#!/usr/bin/python
import Queue
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print "Starting " + self.name
process_data(self.name, self.q)
print "Exiting " + self.name
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print "%s processing %s" % (threadName, data)
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1
# Create new threads
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# Fill the queue
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# Wait for queue to empty
while not workQueue.empty():
pass
# Notify threads it's time to exit
exitFlag = 1
# Wait for all threads to complete
for t in threads:
t.join()
print "Exiting Main Thread"
هنگامی که کد بالا اجرا میشود، نتیجه زیر را تولید میکند:
Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread