آموزش ارتباط رشته ها در همزمانی پایتون

3 سال پیش
آموزش ارتباط رشته ها در همزمانی پایتون

آموزش ارتباط رشته ها در همزمانی پایتون

در این درس از مجموعه آموزش برنامه نویسی سایت سورس باران، به آموزش ارتباط رشته ها در همزمانی پایتون خواهیم پرداخت.

پیشنهاد ویژه : پکیج آموزش طراحی وب سایت با پایتون و جنگو

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

ساختار داده های پایتون برای ارتباط رشته ایمن

کد چند رشته ای با مشکل انتقال اطلاعات از یک موضوع به موضوع دیگر مواجه است. ابتدای ارتباطات استاندارد این مسئله را حل نمی کند. از این رو، ما باید شی کامپوزیت خود را پیاده سازی کنیم تا اشیا را بین رشته ها به اشتراک بگذاریم تا موضوع ارتباطی ایمن باشد. در زیر چند ساختار داده وجود دارد، که پس از ایجاد تغییراتی در آنها ارتباطات ایمنی را ایجاد می کند –

مجموعه ها

برای استفاده از ساختار داده مجموعه ای به روشی بدون موضوع، ما باید کلاس مجموعه را گسترش دهیم تا مکانیسم قفل سازی خودمان را پیاده سازی کنیم.

مثال

در اینجا یک مثال پایتون برای گسترش کلاس وجود دارد –
class extend_class(set):
   def __init__(self, *args, **kwargs):
      self._lock = Lock()
      super(extend_class, self).__init__(*args, **kwargs)

   def add(self, elem):
      self._lock.acquire()
      try:
      super(extend_class, self).add(elem)
      finally:
      self._lock.release()
  
   def delete(self, elem):
      self._lock.acquire()
      try:
      super(extend_class, self).delete(elem)
      finally:
      self._lock.release()
در مثال فوق، یک شی کلاس به نام extend_class تعریف شده است که بیشتر از کلاس مجموعه پایتون به ارث می رسد. یک شی lock در سازنده این کلاس ایجاد می شود. اکنون، دو عملکرد وجود دارد – ()add و ()delete. این توابع تعریف شده و از نظر رشته ایمن هستند.

دکوراتور

این روش کلیدی دیگر برای ارتباطات رشته ایمن استفاده از دکوراتور است.

مثال

def lock_decorator(method):

   def new_deco_method(self, *args, **kwargs):
      with self._lock:
         return method(self, *args, **kwargs)
return new_deco_method

class Decorator_class(set):
   def __init__(self, *args, **kwargs):
      self._lock = Lock()
      super(Decorator_class, self).__init__(*args, **kwargs)

   @lock_decorator
   def add(self, *args, **kwargs):
      return super(Decorator_class, self).add(elem)
   @lock_decorator
   def delete(self, *args, **kwargs):
      return super(Decorator_class, self).delete(elem)
در مثال بالا، یک روش دکوراتور به نام lock_decorator تعریف شده است که بیشتر از کلاس متد Python به ارث می رسد. سپس یک شی lock در سازنده این کلاس ایجاد می شود. اکنون ، دو عملکرد وجود دارد – ()add و ()delete. این توابع تعریف شده و از نظر نخ ایمن هستند.

لیست ها در ارتباط رشته ها در همزمانی پایتون 

ساختار داده های لیست از نظر رشته، سریع و همچنین ساختار آسان برای ذخیره سازی موقت در حافظه است. در Cpython ،GIL از دسترسی همزمان به آنها محافظت می کند. همانطور که دانستیم لیست ها رشته ایمن هستند اما در مورد داده های موجود در آنها چطور؟ در واقع، داده های لیست محافظت نمی شوند.
برای حل این نوع مشکلات و اصلاح رشته ایمن داده ها، باید یک مکانیزم قفل گذاری مناسب را پیاده سازی کنیم، که این اطمینان را می دهد که چندین رشته به طور بالقوه نمی توانند در race conditions قرار بگیرند. برای اجرای مکانیسم قفل گذاری مناسب، می توانیم کلاس را مانند نمونه های قبلی گسترش دهیم.
L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.field = y
D[x] = y
D1.update(D2)
D.keys()

 

  • L,L1,L2 all are lists
  • D,D1,D2 are dicts
  • x,y are objects
  • i, j are ints

صف

اگر داده های لیست محافظت نشوند، ممکن است با عواقب آن مواجه شویم. ما ممکن است از race conditions، مورد داده اشتباهی دریافت کنیم یا حذف کنیم. به همین دلیل توصیه می شود از ساختار داده های صف استفاده کنید. یک نمونه از صف های واقعی می تواند یک جاده یک طرفه با یک خط باشد، جایی که وسیله نقلیه ابتدا وارد می شود، سپس ابتدا خارج می شود. نمونه های واقعی تری از صف های موجود در پنجره های بلیط و ایستگاه های اتوبوس را می توان مشاهده کرد.
Queues
صف ها به طور پیش فرض، ساختار داده ایمن رشته هستند و ما نگران اجرای مکانیسم قفل پیچیده نیستیم. پایتون ماژول ما را برای استفاده از انواع مختلف صف در برنامه ما فراهم می کند.

انواع صف در ارتباط رشته ها در همزمانی پایتون 

در این بخش، ما در مورد انواع مختلف صف اطلاعات کسب خواهیم کرد. پایتون سه گزینه صف برای استفاده از ماژول <queue> فراهم می کند –
  • صف های عادی (FIFO
  • LIFO
  • اولویت
در بخشهای بعدی با صفهای مختلف آشنا خواهیم شد.

صف های عادی (FIFO)

صف پیاده سازی ارائه شده توسط پایتون به طور معمول مورد استفاده قرار می گیرد. در این مکانیزم صف بندی هر کسی که اول شود، ابتدا خدمات را دریافت می کند. به FIFO صف های عادی نیز گفته می شود. صف های FIFO را می توان به شرح زیر نشان داد –
FIFO

اجرای پایتون از صف FIFO

در پایتون، صف FIFO را می توان با تک رشته و همچنین چند رشته اجرا کرد.

صف FIFO با تک رشته

برای اجرای صف FIFO با تک رشته، کلاس Queue یک کانتینر اولیه از ابتدا و از اول را اجرا می کند. عناصر با استفاده از ()put به یک “انتهای” دنباله اضافه می شوند و با استفاده از ()get از انتهای دیگر حذف می شوند.

مثال

در زیر یک برنامه پایتون برای اجرای صف FIFO با یک موضوع وجود دارد –
import queue

q = queue.Queue()

for i in range(8):
   q.put("item-" + str(i))

while not q.empty():
   print (q.get(), end = " ")

خروجی

item-0 item-1 item-2 item-3 item-4 item-5 item-6 item-7
خروجی نشان می دهد که برنامه فوق با استفاده از یک رشته واحد نشان می دهد که عناصر به همان ترتیب وارد شده از صف حذف می شوند.

صف FIFO با چندین رشته

برای پیاده سازی FIFO با چندین رشته، باید تابع ()myqueue را تعریف کنیم که از ماژول صف گسترش یافته است. کارکرد روش های ()get و ()put  همان مواردی است که در بالا در هنگام اجرای صف FIFO با تک رشته مورد بحث قرار گرفت. سپس برای ساختن آن چند رشته ای، باید رشته ها را اعلام و نمونه سازی کنیم. این رشته ها صف را به صورت FIFO مصرف می کنند.

مثال

در زیر یک برنامه پایتون برای اجرای صف FIFO با چندین موضوع وجود دارد
import threading
import queue
import random
import time
def myqueue(queue):
   while not queue.empty():
   item = queue.get()
   if item is None:
   break
   print("{} removed {} from the queue".format(threading.current_thread(), item))
   queue.task_done()
   time.sleep(2)
q = queue.Queue()
for i in range(5):
   q.put(i)
threads = []
for i in range(4):
   thread = threading.Thread(target=myqueue, args=(q,))
   thread.start()
   threads.append(thread)
for thread in threads:
   thread.join()

خروجی

<Thread(Thread-3654, started 5044)> removed 0 from the queue
<Thread(Thread-3655, started 3144)> removed 1 from the queue
<Thread(Thread-3656, started 6996)> removed 2 from the queue
<Thread(Thread-3657, started 2672)> removed 3 from the queue
<Thread(Thread-3654, started 5044)> removed 4 from the queue

 

LIFO 

این صف کاملاً برخلاف صف های FIFO است. در این مکانیزم صف بندی، کسی که آخرین باشد، ابتدا خدمات را دریافت می کند. این مورد مشابه پیاده سازی ساختار داده های پشته است. صف های LIFO هنگام اجرای جستجوی عمق اول مانند الگوریتم های هوش مصنوعی مفید هستند.

اجرای پایتون از صف LIFO

در پایتون، صف LIFO را می توان با تک رشته و همچنین چند رشته اجرا کرد.

صف LIFO با تک موضوع

برای اجرای صف LIFO با تک رشته، کلاس Queue با استفاده از ساختار Queue.LifoQueue یک کانتینر اساسی آخرین و اولین بار را اجرا می کند. اکنون، با فراخوانی () put، عناصر در سر ظرف اضافه می شوند و همچنین با استفاده از ()get از سر جدا می شوند.

مثال

در زیر یک برنامه پایتون برای اجرای صف LIFO با یک رشته وجود دارد –
import queue

q = queue.LifoQueue()

for i in range(8):
   q.put("item-" + str(i))

while not q.empty():
   print (q.get(), end=" ")
Output:
item-7 item-6 item-5 item-4 item-3 item-2 item-1 item-0
خروجی نشان می دهد که برنامه فوق از یک موضوع واحد برای نشان دادن حذف عناصر از صف به ترتیب مخالف وارد شده استفاده می کند.

صف LIFO با چندین رشته

همانطور که ما اجرای صف های FIFO را با چندین رشته انجام داده ایم ، این اجرا مشابه است. تنها تفاوت در این است که ما باید از کلاس Queue استفاده کنیم که با استفاده از ساختار Queue.LifoQueue یک کانتینر اولیه در آخرین و اولین بار را پیاده سازی می کند.

مثال

در زیر یک برنامه پایتون برای اجرای صف LIFO با چندین رشته وجود دارد –
import threading
import queue
import random
import time
def myqueue(queue):
   while not queue.empty():
      item = queue.get()
      if item is None:
      break
      print("{} removed {} from the queue".format(threading.current_thread(), item))
      queue.task_done()
      time.sleep(2)
q = queue.LifoQueue()
for i in range(5):
   q.put(i)
threads = []
for i in range(4):
   thread = threading.Thread(target=myqueue, args=(q,))
   thread.start()
   threads.append(thread)
for thread in threads:
   thread.join()

خروجی

<Thread(Thread-3882, started 4928)> removed 4 from the queue
<Thread(Thread-3883, started 4364)> removed 3 from the queue
<Thread(Thread-3884, started 6908)> removed 2 from the queue
<Thread(Thread-3885, started 3584)> removed 1 from the queue
<Thread(Thread-3882, started 4928)> removed 0 from the queue

 

صف اولویت

در صف های FIFO و LIFO ، ترتیب موارد مربوط به ترتیب درج است. با این حال، موارد بسیاری وجود دارد که اولویت مهمتر از ترتیب درج است. بیایید یک مثال از دنیای واقعی را در نظر بگیریم. فرض کنید امنیت در فرودگاه افراد مختلفی را بررسی می کند. دسته های VVIP، کارکنان هواپیمایی، افسر، دسته ها ممکن است به جای اینکه بر اساس ورود بررسی شوند، در اولویت بررسی می شوند.
جنبه مهم دیگری که باید برای صف اولویت در نظر گرفته شود، نحوه ایجاد برنامه زمانبندی کار است. یک طراحی معمول این است که بیشترین وظیفه را بر اساس اولویت در صف انجام دهیم. از این ساختار داده می توان برای انتخاب موارد از صف بر اساس مقدار اولویت آنها استفاده کرد.

اجرای پایتون از صف اولویت

در پایتون، صف اولویت را می توان با تک رشته و همچنین چند رشته اجرا کرد.

صف اولویت با تک رشته

برای اجرای صف اولویت با تک رشته، کلاس Queue با استفاده از ساختار Queue.PriorityQueue وظیفه ای را در ظرف اولویت دار پیاده سازی می کند. اکنون، با فراخوانی ()put، عناصر با مقداری اضافه می شوند که کمترین مقدار دارای بالاترین اولویت باشد و از این رو ابتدا با استفاده از ()get بازیابی می شود.

مثال

برنامه پایتون زیر را برای اجرای صف اولویت با یک رشته در نظر بگیرید –
import queue as Q
p_queue = Q.PriorityQueue()

p_queue.put((2, 'Urgent'))
p_queue.put((1, 'Most Urgent'))
p_queue.put((10, 'Nothing important'))
prio_queue.put((5, 'Important'))

while not p_queue.empty():
   item = p_queue.get()
   print('%s - %s' % item)

 

خروجی

۱ – Most Urgent
۲ - Urgent
۵ - Important
۱۰ – Nothing important

 

در خروجی فوق ، می بینیم که صف موارد را بر اساس اولویت ذخیره کرده است – مقدار کمتری دارای اولویت بالا است.

صف اولویت با چند رشته

پیاده سازی مشابه اجرای صف های FIFO و LIFO با چندین رشته است. تنها تفاوت این است که ما برای شروع اولویت با استفاده از ساختار Queue.PriorityQueue باید از کلاس Queue استفاده کنیم. تفاوت دیگر با نحوه ایجاد صف است. در مثالی که در زیر آورده شده است ، با دو مجموعه داده یکسان تولید می شود.

مثال

برنامه پایتون زیر با اجرای چندین رشته به اجرای صف اولویت کمک می کند –
import threading
import queue
import random
import time
def myqueue(queue):
   while not queue.empty():
      item = queue.get()
      if item is None:
      break
      print("{} removed {} from the queue".format(threading.current_thread(), item))
      queue.task_done()
      time.sleep(1)
q = queue.PriorityQueue()
for i in range(5):
   q.put(i,1)

for i in range(5):
   q.put(i,1)

threads = []
for i in range(2):
   thread = threading.Thread(target=myqueue, args=(q,))
   thread.start()
   threads.append(thread)
for thread in threads:
   thread.join()

 

خروجی

<Thread(Thread-4939, started 2420)> removed 0 from the queue
<Thread(Thread-4940, started 3284)> removed 0 from the queue
<Thread(Thread-4939, started 2420)> removed 1 from the queue
<Thread(Thread-4940, started 3284)> removed 1 from the queue
<Thread(Thread-4939, started 2420)> removed 2 from the queue
<Thread(Thread-4940, started 3284)> removed 2 from the queue
<Thread(Thread-4939, started 2420)> removed 3 from the queue
<Thread(Thread-4940, started 3284)> removed 3 from the queue
<Thread(Thread-4939, started 2420)> removed 4 from the queue
<Thread(Thread-4940, started 3284)> removed 4 from the queue

 

منبع.

لیست جلسات قبل آموزش همزبانی در پایتون

  1. آموزش همزمانی در برنامه نویسی پایتون
  2. آموزش همزمانی در برنامه نویسی پایتون _ مقدمه
  3. آموزش معماری سیستم و حافظه در برنامه نویسی پایتون
  4.  آموزش رشته ها در همزمانی پایتون 
  5. آموزش پیاده سازی رشته در همزمانی پایتون
  6. آموزش همگام سازی رشته ها در همزمانی پایتون
0
برچسب ها :
نویسنده مطلب erfan molaei

دیدگاه شما

بدون دیدگاه