Sinkronisasi Thread
Sinkronisasi utas dapat didefinisikan sebagai metode dengan bantuan yang kami yakinkan bahwa dua atau lebih utas bersamaan tidak secara bersamaan mengakses segmen program yang dikenal sebagai bagian kritis. Di sisi lain, seperti yang kita ketahui bahwa bagian kritis adalah bagian dari program di mana sumber daya bersama diakses. Oleh karena itu kita dapat mengatakan bahwa sinkronisasi adalah proses untuk memastikan bahwa dua atau lebih utas tidak saling berinteraksi dengan mengakses sumber daya pada saat yang bersamaan. Diagram di bawah ini menunjukkan bahwa empat thread mencoba mengakses bagian kritis dari suatu program pada saat yang bersamaan.
Untuk membuatnya lebih jelas, misalkan dua atau lebih utas mencoba menambahkan objek dalam daftar pada saat yang bersamaan. Tindakan ini tidak dapat berakhir dengan sukses karena tindakan ini akan menjatuhkan satu atau semua objek atau akan sepenuhnya merusak status daftar. Di sini peran sinkronisasi adalah hanya satu utas pada satu waktu yang dapat mengakses daftar.
Masalah dalam sinkronisasi utas
Kami mungkin mengalami masalah saat menerapkan pemrograman bersamaan atau menerapkan sinkronisasi primitif. Pada bagian ini, kita akan membahas dua masalah utama. Masalahnya adalah -
- Deadlock
- Kondisi balapan
Kondisi balapan
Ini adalah salah satu masalah utama dalam pemrograman bersamaan. Akses bersamaan ke sumber daya bersama dapat menyebabkan kondisi balapan. Kondisi balapan dapat didefinisikan sebagai terjadinya kondisi ketika dua atau lebih utas dapat mengakses data bersama dan kemudian mencoba mengubah nilainya pada saat yang sama. Karena itu, nilai variabel mungkin tidak dapat diprediksi dan bervariasi tergantung pada pengaturan waktu sakelar konteks dari proses.
Contoh
Pertimbangkan contoh ini untuk memahami konsep kondisi balapan -
Step 1 - Pada langkah ini, kita perlu mengimpor modul threading -
import threading
Step 2 - Sekarang, tentukan variabel global, katakanlah x, beserta nilainya sebagai 0 -
x = 0
Step 3 - Sekarang, kita perlu mendefinisikan file increment_global() fungsi, yang akan melakukan kenaikan sebesar 1 dalam fungsi global ini x -
def increment_global():
global x
x += 1
Step 4 - Pada langkah ini, kita akan mendefinisikan file taskofThread()fungsi, yang akan memanggil fungsi increment_global () untuk beberapa kali; untuk contoh kita ini adalah 50000 kali -
def taskofThread():
for _ in range(50000):
increment_global()
Step 5- Sekarang, tentukan fungsi main () di mana utas t1 dan t2 dibuat. Keduanya akan dimulai dengan bantuan fungsi start () dan menunggu sampai mereka menyelesaikan pekerjaannya dengan bantuan fungsi join ().
def main():
global x
x = 0
t1 = threading.Thread(target= taskofThread)
t2 = threading.Thread(target= taskofThread)
t1.start()
t2.start()
t1.join()
t2.join()
Step 6- Sekarang, kita perlu memberikan range seperti berapa banyak iterasi yang ingin kita panggil fungsi main (). Di sini, kami memanggilnya sebanyak 5 kali.
if __name__ == "__main__":
for i in range(5):
main()
print("x = {1} after Iteration {0}".format(i,x))
Pada output yang ditunjukkan di bawah ini, kita dapat melihat pengaruh kondisi balapan sebagai nilai x setelah setiap iterasi diharapkan 100000. Namun, ada banyak variasi dalam nilainya. Hal ini disebabkan oleh akses bersamaan utas ke variabel global bersama x.
Keluaran
x = 100000 after Iteration 0
x = 54034 after Iteration 1
x = 80230 after Iteration 2
x = 93602 after Iteration 3
x = 93289 after Iteration 4
Menangani kondisi balapan menggunakan kunci
Seperti yang telah kita lihat efek dari kondisi balapan pada program di atas, maka diperlukan sebuah alat sinkronisasi, yang dapat menangani kondisi balapan antara beberapa thread. Dengan Python, file<threading>modul menyediakan kelas Lock untuk menangani kondisi balapan. Selanjutnya,Lockclass menyediakan metode yang berbeda dengan bantuannya kita dapat menangani kondisi balapan antara beberapa utas. Metodenya dijelaskan di bawah ini -
memperoleh ()
Metode ini digunakan untuk memperoleh, yaitu memblokir kunci. Kunci dapat memblokir atau tidak memblokir tergantung pada nilai benar atau salah berikut -
With value set to True - Jika metode perolehan () dipanggil dengan True, yang merupakan argumen default, maka eksekusi utas akan diblokir hingga kunci dibuka.
With value set to False - Jika metode perolehan () dipanggil dengan False, yang bukan merupakan argumen default, maka eksekusi utas tidak akan diblokir hingga disetel ke true, yaitu, hingga terkunci.
release ()
Metode ini digunakan untuk membuka kunci. Berikut adalah beberapa tugas penting yang terkait dengan metode ini -
Jika kunci terkunci, maka release()metode akan membukanya. Tugasnya adalah memungkinkan tepat satu utas untuk melanjutkan jika lebih dari satu utas diblokir dan menunggu kunci dibuka kuncinya.
Ini akan meningkatkan a ThreadError jika kunci sudah terbuka.
Sekarang, kita dapat menulis ulang program di atas dengan kelas kunci dan metodenya untuk menghindari kondisi balapan. Kita perlu mendefinisikan metode taskofThread () dengan argumen kunci dan kemudian perlu menggunakan metode perolehan () dan rilis () untuk memblokir dan non-pemblokiran kunci untuk menghindari kondisi balapan.
Contoh
Berikut contoh program python untuk memahami konsep gembok untuk menangani kondisi balapan -
import threading
x = 0
def increment_global():
global x
x += 1
def taskofThread(lock):
for _ in range(50000):
lock.acquire()
increment_global()
lock.release()
def main():
global x
x = 0
lock = threading.Lock()
t1 = threading.Thread(target = taskofThread, args = (lock,))
t2 = threading.Thread(target = taskofThread, args = (lock,))
t1.start()
t2.start()
t1.join()
t2.join()
if __name__ == "__main__":
for i in range(5):
main()
print("x = {1} after Iteration {0}".format(i,x))
Output berikut menunjukkan bahwa efek kondisi balapan diabaikan; karena nilai x, setelah setiap & setiap iterasi, sekarang menjadi 100000, sesuai dengan ekspektasi program ini.
Keluaran
x = 100000 after Iteration 0
x = 100000 after Iteration 1
x = 100000 after Iteration 2
x = 100000 after Iteration 3
x = 100000 after Iteration 4
Deadlocks - Masalah Dining Philosophers
Kebuntuan adalah masalah menyusahkan yang dapat dihadapi seseorang saat merancang sistem bersamaan. Kami dapat mengilustrasikan masalah ini dengan bantuan masalah filsuf makan sebagai berikut -
Edsger Dijkstra awalnya memperkenalkan masalah filsuf makan, salah satu ilustrasi terkenal dari salah satu masalah terbesar sistem konkuren yang disebut kebuntuan.
Dalam soal ini, ada lima filsuf terkenal yang duduk di meja bundar sambil makan makanan dari mangkuk mereka. Ada lima garpu yang bisa digunakan oleh lima filsuf untuk menyantap makanannya. Namun, para filsuf memutuskan untuk menggunakan dua garpu pada saat bersamaan untuk menyantap makanan mereka.
Sekarang, ada dua syarat utama bagi para filsuf. Pertama, masing-masing filsuf bisa makan atau berpikir dan kedua, mereka harus terlebih dahulu mendapatkan kedua garpu, yaitu kiri dan kanan. Masalah muncul ketika masing-masing dari lima filsuf berhasil memilih pertigaan kiri pada saat bersamaan. Sekarang mereka semua menunggu garpu yang tepat untuk dibebaskan tetapi mereka tidak akan pernah melepaskan garpu mereka sampai mereka makan dan garpu yang tepat tidak akan pernah tersedia. Makanya, akan ada keadaan buntu di meja makan.
Kebuntuan dalam sistem konkuren
Sekarang jika kita lihat, masalah yang sama bisa muncul di sistem bersamaan kita juga. Garpu dalam contoh di atas akan menjadi sumber daya sistem dan setiap filsuf dapat mewakili proses, yang bersaing untuk mendapatkan sumber daya.
Solusi dengan program Python
Solusi dari masalah ini dapat ditemukan dengan membagi filsuf menjadi dua jenis - greedy philosophers dan generous philosophers. Terutama seorang filsuf yang rakus akan mencoba mengambil garpu kiri dan menunggu sampai garpu itu ada. Dia kemudian akan menunggu garpu yang tepat berada di sana, mengambilnya, memakannya dan kemudian meletakkannya. Di sisi lain, seorang filsuf yang murah hati akan mencoba mengambil garpu kiri dan jika tidak ada, dia akan menunggu dan mencoba lagi setelah beberapa waktu. Jika mereka mendapatkan garpu kiri maka mereka akan mencoba untuk mendapatkan garpu yang benar. Jika mereka juga mendapatkan garpu yang tepat, mereka akan makan dan melepaskan kedua garpu tersebut. Namun, jika mereka tidak mendapatkan garpu kanan maka mereka akan melepaskan garpu kiri.
Contoh
Program Python berikut akan membantu kami menemukan solusi untuk masalah filsuf makan -
import threading
import random
import time
class DiningPhilosopher(threading.Thread):
running = True
def __init__(self, xname, Leftfork, Rightfork):
threading.Thread.__init__(self)
self.name = xname
self.Leftfork = Leftfork
self.Rightfork = Rightfork
def run(self):
while(self.running):
time.sleep( random.uniform(3,13))
print ('%s is hungry.' % self.name)
self.dine()
def dine(self):
fork1, fork2 = self.Leftfork, self.Rightfork
while self.running:
fork1.acquire(True)
locked = fork2.acquire(False)
if locked: break
fork1.release()
print ('%s swaps forks' % self.name)
fork1, fork2 = fork2, fork1
else:
return
self.dining()
fork2.release()
fork1.release()
def dining(self):
print ('%s starts eating '% self.name)
time.sleep(random.uniform(1,10))
print ('%s finishes eating and now thinking.' % self.name)
def Dining_Philosophers():
forks = [threading.Lock() for n in range(5)]
philosopherNames = ('1st','2nd','3rd','4th', '5th')
philosophers= [DiningPhilosopher(philosopherNames[i], forks[i%5], forks[(i+1)%5]) \
for i in range(5)]
random.seed()
DiningPhilosopher.running = True
for p in philosophers: p.start()
time.sleep(30)
DiningPhilosopher.running = False
print (" It is finishing.")
Dining_Philosophers()
Program di atas menggunakan konsep filsuf yang rakus dan murah hati. Program ini juga telah menggunakanacquire() dan release() metode dari Lock kelas dari <threading>modul. Kita dapat melihat solusinya pada output berikut -
Keluaran
4th is hungry.
4th starts eating
1st is hungry.
1st starts eating
2nd is hungry.
5th is hungry.
3rd is hungry.
1st finishes eating and now thinking.3rd swaps forks
2nd starts eating
4th finishes eating and now thinking.
3rd swaps forks5th starts eating
5th finishes eating and now thinking.
4th is hungry.
4th starts eating
2nd finishes eating and now thinking.
3rd swaps forks
1st is hungry.
1st starts eating
4th finishes eating and now thinking.
3rd starts eating
5th is hungry.
5th swaps forks
1st finishes eating and now thinking.
5th starts eating
2nd is hungry.
2nd swaps forks
4th is hungry.
5th finishes eating and now thinking.
3rd finishes eating and now thinking.
2nd starts eating 4th starts eating
It is finishing.