Threads Intercommunication
Dalam kehidupan nyata, jika sekelompok orang mengerjakan tugas bersama maka harus ada komunikasi di antara mereka untuk menyelesaikan tugas dengan benar. Analogi yang sama juga berlaku untuk utas. Dalam pemrograman, untuk mengurangi waktu ideal prosesor, kami membuat beberapa utas dan menetapkan sub tugas yang berbeda ke setiap utas. Oleh karena itu, harus ada fasilitas komunikasi dan mereka harus saling berinteraksi untuk menyelesaikan pekerjaannya secara sinkron.
Pertimbangkan poin penting berikut terkait dengan pergaulan utas -
No performance gain - Jika kami tidak dapat mencapai komunikasi yang tepat antara utas dan proses, maka kinerja yang diperoleh dari konkurensi dan paralelisme tidak ada gunanya.
Accomplish task properly - Tanpa mekanisme interkomunikasi yang tepat antar utas, tugas yang diberikan tidak dapat diselesaikan dengan benar.
More efficient than inter-process communication - Komunikasi antar-utas lebih efisien dan mudah digunakan daripada komunikasi antar-proses karena semua utas dalam suatu proses berbagi ruang alamat yang sama dan mereka tidak perlu menggunakan memori bersama.
Struktur data Python untuk komunikasi thread-safe
Kode multithread muncul dengan masalah meneruskan informasi dari satu utas ke utas lainnya. Primitif komunikasi standar tidak menyelesaikan masalah ini. Oleh karena itu, kita perlu mengimplementasikan objek komposit kita sendiri untuk berbagi objek di antara utas agar komunikasi aman untuk utas. Berikut adalah beberapa struktur data, yang menyediakan komunikasi thread-safe setelah membuat beberapa perubahan di dalamnya -
Set
Untuk menggunakan struktur data set dengan cara yang aman untuk thread, kita perlu memperluas kelas set untuk mengimplementasikan mekanisme penguncian kita sendiri.
Contoh
Berikut adalah contoh Python untuk memperluas kelas -
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()
Dalam contoh di atas, sebuah objek kelas bernama extend_class telah didefinisikan yang selanjutnya diwarisi dari Python set class. Objek kunci dibuat dalam konstruktor kelas ini. Sekarang, ada dua fungsi -add() dan delete(). Fungsi ini ditentukan dan aman untuk thread. Mereka berdua mengandalkan filesuper fungsionalitas kelas dengan satu pengecualian kunci.
Penghias
Ini adalah metode kunci lain untuk komunikasi thread-safe adalah penggunaan dekorator.
Contoh
Pertimbangkan contoh Python yang menunjukkan bagaimana menggunakan dekorator & mminus;
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)
Dalam contoh di atas, metode dekorator bernama lock_decorator telah didefinisikan yang selanjutnya diwarisi dari kelas metode Python. Kemudian objek kunci dibuat dalam konstruktor kelas ini. Sekarang, ada dua fungsi - add () dan delete (). Fungsi ini ditentukan dan aman untuk thread. Keduanya mengandalkan fungsionalitas kelas super dengan satu pengecualian kunci.
Daftar
Struktur data daftar adalah struktur yang aman untuk thread, cepat, dan mudah untuk penyimpanan sementara dalam memori. Di Cpython, GIL melindungi dari akses bersamaan ke mereka. Seperti yang kita ketahui bahwa daftar aman untuk thread tetapi bagaimana dengan data yang ada di dalamnya. Sebenarnya, data daftar tidak dilindungi. Sebagai contoh,L.append(x)tidak menjamin untuk mengembalikan hasil yang diharapkan jika utas lain mencoba melakukan hal yang sama. Ini karena, meskipunappend() adalah operasi atomik dan thread-safe, tetapi thread lain mencoba mengubah data daftar secara bersamaan sehingga kita dapat melihat efek samping kondisi balapan pada keluaran.
Untuk mengatasi masalah semacam ini dan mengubah data dengan aman, kita harus menerapkan mekanisme penguncian yang tepat, yang selanjutnya memastikan bahwa beberapa utas tidak berpotensi mengalami kondisi balapan. Untuk mengimplementasikan mekanisme penguncian yang tepat, kita dapat memperluas kelas seperti yang kita lakukan pada contoh sebelumnya.
Beberapa operasi atom lainnya pada daftar adalah sebagai berikut -
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()
Di sini -
- L, L1, L2 semuanya adalah daftar
- D, D1, D2 adalah penis
- x, y adalah objek
- i, j are ints
Antrian
Jika data daftar tidak dilindungi, kami mungkin harus menghadapi konsekuensinya. Kami mungkin mendapatkan atau menghapus item data yang salah, dari kondisi balapan. Itulah mengapa disarankan untuk menggunakan struktur data antrian. Contoh antrian dunia nyata dapat berupa jalan satu arah satu jalur, di mana kendaraan masuk lebih dulu, keluar lebih dulu. Contoh dunia nyata lainnya dapat dilihat dari antrian di loket tiket dan halte bus.
Antrian secara default, struktur data thread-safe dan kita tidak perlu khawatir tentang penerapan mekanisme penguncian yang kompleks. Python memberi kita file
Jenis Antrian
Di bagian ini, kita akan mendapatkan tentang berbagai jenis antrian. Python menyediakan tiga opsi antrian untuk digunakan dari<queue> modul -
- Antrian Normal (FIFO, First in First out)
- LIFO, Last in First Out
- Priority
Kita akan mempelajari tentang antrian yang berbeda di bagian selanjutnya.
Antrian Normal (FIFO, First in First out)
Ini adalah implementasi antrian paling umum yang ditawarkan oleh Python. Dalam mekanisme antrian ini siapapun yang datang lebih dulu, akan mendapatkan layanan terlebih dahulu. FIFO juga disebut antrian normal. Antrian FIFO dapat direpresentasikan sebagai berikut -
Implementasi Python Antrian FIFO
Dalam python, antrian FIFO dapat diimplementasikan dengan utas tunggal maupun multithread.
Antrian FIFO dengan utas tunggal
Untuk mengimplementasikan antrian FIFO dengan single thread, file Queuekelas akan menerapkan wadah dasar pertama masuk, keluar pertama. Elemen akan ditambahkan ke salah satu "akhir" dari urutan menggunakanput(), dan dihapus dari ujung lainnya menggunakan get().
Contoh
Berikut adalah program Python untuk implementasi antrian FIFO dengan utas tunggal -
import queue
q = queue.Queue()
for i in range(8):
q.put("item-" + str(i))
while not q.empty():
print (q.get(), end = " ")
Keluaran
item-0 item-1 item-2 item-3 item-4 item-5 item-6 item-7
Outputnya menunjukkan bahwa program di atas menggunakan thread tunggal untuk menggambarkan bahwa elemen dihapus dari antrian dengan urutan yang sama ketika dimasukkan.
Antrian FIFO dengan banyak utas
Untuk mengimplementasikan FIFO dengan banyak utas, kita perlu mendefinisikan fungsi myqueue (), yang diperluas dari modul antrian. Cara kerja metode get () dan put () sama seperti yang dibahas di atas saat menerapkan antrian FIFO dengan utas tunggal. Kemudian untuk membuatnya multithread, kita perlu mendeklarasikan dan membuat instance utasnya. Utas ini akan menggunakan antrian dengan cara FIFO.
Contoh
Berikut ini adalah program Python untuk implementasi antrian FIFO dengan banyak utas
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()
Keluaran
<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, Last in First Out antrian
Antrian ini menggunakan analogi yang sangat berlawanan dengan antrian FIFO (First in First Out). Dalam mekanisme antrian ini, orang yang datang terakhir akan mendapatkan pelayanan terlebih dahulu. Ini mirip dengan mengimplementasikan struktur data tumpukan. Antrean LIFO terbukti berguna saat menerapkan penelusuran Depth-first seperti algoritme kecerdasan buatan.
Implementasi Python dari antrian LIFO
Dalam python, antrian LIFO dapat diimplementasikan dengan single thread dan juga multithread.
Antrian LIFO dengan utas tunggal
Untuk mengimplementasikan antrian LIFO dengan single thread, file Queue kelas akan mengimplementasikan wadah dasar terakhir masuk, keluar pertama dengan menggunakan struktur Queue.LifoQueue. Sekarang, saat meneleponput(), elemen ditambahkan di kepala wadah dan dikeluarkan dari kepala juga saat digunakan get().
Contoh
Berikut adalah program Python untuk implementasi antrian LIFO dengan utas tunggal -
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
Outputnya menunjukkan bahwa program di atas menggunakan utas tunggal untuk menggambarkan bahwa elemen dihapus dari antrian dengan urutan yang berlawanan ketika dimasukkan.
Antrian LIFO dengan banyak utas
Implementasinya mirip seperti yang kita lakukan pada implementasi antrian FIFO dengan banyak utas. Satu-satunya perbedaan adalah kita perlu menggunakanQueue kelas yang akan mengimplementasikan wadah dasar terakhir masuk, keluar pertama dengan menggunakan struktur Queue.LifoQueue.
Contoh
Berikut ini adalah program Python untuk implementasi antrian LIFO dengan banyak utas -
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()
Keluaran
<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
Antrian prioritas
Dalam antrian FIFO dan LIFO, urutan item terkait dengan urutan penyisipan. Namun, ada banyak kasus ketika prioritas lebih penting daripada urutan penyisipan. Mari kita pertimbangkan contoh dunia nyata. Misalkan keamanan di bandara memeriksa orang-orang dari kategori yang berbeda. Orang-orang VVIP, staf maskapai penerbangan, petugas bea cukai, kategori dapat diperiksa berdasarkan prioritas daripada diperiksa berdasarkan kedatangan seperti untuk rakyat jelata.
Aspek penting lainnya yang perlu dipertimbangkan untuk antrian prioritas adalah bagaimana mengembangkan penjadwal tugas. Salah satu desain umum adalah untuk melayani tugas paling agen berdasarkan prioritas dalam antrian. Struktur data ini dapat digunakan untuk mengambil item dari antrian berdasarkan nilai prioritasnya.
Implementasi Python Antrian Prioritas
Dalam python, antrian prioritas dapat diimplementasikan dengan utas tunggal serta multithread.
Antrian prioritas dengan utas tunggal
Untuk mengimplementasikan antrian prioritas dengan single thread, file Queue kelas akan mengimplementasikan tugas pada kontainer prioritas dengan menggunakan struktur Queue.PriorityQueue. Sekarang, saat meneleponput(), elemen ditambahkan dengan nilai di mana nilai terendah akan memiliki prioritas tertinggi dan karenanya diambil pertama kali dengan menggunakan get().
Contoh
Pertimbangkan program Python berikut untuk implementasi antrian Prioritas dengan utas tunggal -
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)
Keluaran
1 – Most Urgent
2 - Urgent
5 - Important
10 – Nothing important
Pada output di atas, kita dapat melihat bahwa antrian telah menyimpan item berdasarkan prioritas - nilai kurang memiliki prioritas tinggi.
Antrian prioritas dengan multi utas
Implementasinya mirip dengan implementasi antrian FIFO dan LIFO dengan banyak utas. Satu-satunya perbedaan adalah kita perlu menggunakanQueue kelas untuk menginisialisasi prioritas dengan menggunakan struktur Queue.PriorityQueue. Perbedaan lainnya adalah dengan cara antrian akan dibuat. Dalam contoh yang diberikan di bawah ini, ini akan dibuat dengan dua kumpulan data yang identik.
Contoh
Program Python berikut membantu dalam implementasi antrian prioritas dengan banyak utas -
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()
Keluaran
<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