شنبه ۲۹ دي ۱۴۰۳
Tut24 آموزش برنامه نویسی و مجله تخصصی فناوری ورود/عضویت

آموزش پایتون - برنامه‌نویسی چندرشته‌ای

اجرای چند رشته مشابه اجرای چند برنامه مختلف به‌طور همزمان است، اما با این مزایا:

  • چندین رشته در یک فرایند فضای داده مشترکی با رشته اصلی دارند و بنابراین می‌توانند اطلاعات را با یکدیگر به‌راحتی به اشتراک بگذارند یا با یکدیگر ارتباط برقرار کنند، بیشتر از آنکه فرایندهای جداگانه باشند.

  • رشته‌ها گاهی اوقات فرایندهای سبک‌وزن نامیده می‌شوند و نیازی به حافظه اضافی زیادی ندارند؛ آنها ارزان‌تر از فرایندها هستند.

یک رشته دارای یک آغاز، یک دنباله اجرا، و یک نتیجه است. آن یک نشانگر دستورالعمل دارد که ردیابی می‌کند که در کجای زمینه خود در حال اجرا است.

  • می‌تواند قطع شود (متوقف شود)

  • می‌تواند به‌طور موقت در حالت تعلیق قرار گیرد (که به آن خوابیدن نیز گفته می‌شود) در حالی که رشته‌های دیگر در حال اجرا هستند - این کار 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