Gambar 19.3. Algoritma III
20.4. Fungsi Semafor
void buka(int sem_value) { sem_value++;
}
Nama asli dari operasi tersebut sebenarnya adalah Proberen (test) dan Verhogen (increment). Namun, sebutan untuk 2 method ini sangat beragam, antara lain sering dikenal dengan nama :release dan acquire, P dan V , serta kunci dan buka. Dalam buku ini akan digunakan istilahbukadankunci. Fungsiwaitdipanggil ketika thread akan memasuki critical section-nya atau ketika thread akan memakai resource yang tersedia. Jika sem_value kurang dari sama dengan 0, thread tersebut harus menunggu sampai thread lain memanggil fungsi buka. Fungsi
bukadipanggil ketika thread meningggalkan critical section-nya atau ketika melepaskan resource yang telah digunakannya. Tentu saja kedua operasi tersebut harus bersifat atomik karena
sem_valuedapat diakses oleh beberapa proses (shared resource). Semafor memiliki dua jenis, yaitu:
1. Binary semaphore. Semafor ini hanya memiliki nilai 1 atau 0. Sering juga disebut sebagai semafor primitif
2. Counting semaphore. Semafor ini memiliki nilai 0, 1, serta integer lainnya. Banyak sistem operasi yang tidak secara langsung mengimplementasikan semafor ini, tetapi dengan memanfaatkan binary semaphore
20.4. Fungsi Semafor
Seperti telah disebutkan sebelumnya, semafor berfungsi untuk menangani masalah sinkronisasi secara umum, yaitu:
1. Mutual Exclusion. Sesuai dengan prinsip mutual exclusion, jika suatu thread sedang berada dalam critical section-nya, thread lain harus menunggu thread tersebut keluar dari critical
section-nya sebelum dapat memasuki critical section-nya sendiri. Di sinilah semafor digunakan, thread yang akan memasuki critical section-nya akan memanggil fungsikunciterlebih dahulu. Jika tidak ada thread lain yang sedang berada dalam critical section, thread ini akan memasuki
critical section-nya. Jika terdapat thread lain yang sedang berada dalam critical section-nya, thread ini harus menunggu.Setelah thread keluar dari critical section-nya, thread tersebut akan
memanggil fungsi buka sehingga sem_value akan naik menjadi lebih dari 0, dan satu (dari beberapa) thread yang sedang menunggu akan mendapatkan giliran untuk memasuki critical
section-nya.
Sebagai contoh, misalnya terdapat dua buah thread yang sedang berjalan bersamaan:
thread A: thread B:
count = count + 1; count = count + 1;
Thread A dan B mengakses variabel yang sama, yaitucount sehingga thread A dan B harus berjalan satu-satu. Untuk itu digunakan semafor mutex yang berupa binary semaphore dengan nilai awal 1.
thread A: thread B: kunci(mutex); kunci(mutex); count = count + 1; count = count + 1; buka(mutex); buka(mutex);
Thread manapun yang mengeksekusikunciterlebih dahulu akan jalan terus, sedangkan thread yang tiba belakangan akan menunggu sampai thread yang sudah berjalan terlebih dahulu
mengeksekusibuka, setelah itu kedua thread berjalan lagi dengan normal.
2. Resource Controller. Bayangkan sebuah restoran yang setiap malamnya ramai dikunjungi pelanggan. Kapasitas restoran terbatas, tetapi pemilik restoran memiliki kebijakan bahwa semua pengunjung yang datang akan mendapatkan kesempatan untuk makan, dengan konsekuensi yaitu pelanggan harus sabar menunggu gilirannya. Oleh karena itu, dikerahkanlah pegawai restoran untuk menahan tamu di luar jika restoran penuh lalu mempersilahkan tamu masuk jika tempat telah tersedia.Dari analogi di atas, pelanggan adalah thread, kapasitas restoran adalah resource, dan pegawai restoran adalah semafor. Semafor menyimpan banyaknya resource yang tersedia. Saat thread ingin memakai resource ia akan memanggil fungsi kunci. Jika resource masih tersedia, thread bisa langsung menggunakannya, sebaliknya jika semua resource sedang dipakai,
thread tersebut harus menunggu. Setelah resource selesai dipakai thread akan memanggil fungsi
bukasehingga resource yang bebas bertambah.
Contohnya dapat kita lihat pada kasus berikut: Terdapat tiga buah thread yang berjalan bersamaan. Resource yang tersedia hanya cukup untuk dua buah thread.
thread A: thread B: thread C:
//critical section //critical section //critical section
Tentu saja harus diatur agar pada suatu saat hanya ada dua buah thread yang berada pada critical
section-nya. Hal ini dilakukan dengan menggunakan semafor multiplex yaitu sebuah counting semaphore dengan nilai awal sama dengan jumlah resource yang tersedia yaitu dua.
thread A: thread B: thread C:
kunci(multiplex); kunci(multiplex); kunci(multiplex); //critical section //critical section //critical section buka(multiplex); buka(multiplex); buka(multiplex);
Jika dua buah thread sedang berada dalam critical section, thread berikutnya yang akan memasuki critical section harus menunggu kedua thread tersebut selesai untuk dapat memasuki
critical section-nya.
3. Sinkronisasi Antar-Proses. Ada kalanya suatu thread memerlukan resource yang dihasilkan oleh thread lainnya. Oleh karena itu dibutuhkan suatu mekanisme untuk mengatur urutan eksekusi thread. Mekanisme ini dilakukan dengan memanfaatkan semafor.
Sebagai contoh, misalnya terdapat dua buah thread yang sedang berjalan bersamaan:
thread A: thread B:
count = count + 1; count = count * 2;
Nilai awal dari variabelcountadalah 5, nilai akhir yang diinginkan adalah 12, oleh karena itu
thread A harus dieksekusi sebelum thread B. Agar hal ini dapat terjadi dibutuhkan suatu semafor mutex yang merupakan sebuah binary semaphore dengan nilai awal 0.
thread A: thread B: count = count + 1; kunci(mutex); buka(mutex); count = count * 2;
Thread B akan menunggu sampai eksekusi thread A selesai sebelum melanjutkan.
Jika kita cermati fungsikunci, thread akan terus berada dalam waiting loop sampai sem_value
naik lebih dari 0. Padahal, di dalam loop tersebut thread tidak melakukan tugas apa-apa. Inilah yang disebut dengan busy waiting (semafor jenis ini disebut dengan semafor spinlock). Hal ini tentu saja akan berakibat buruk terhadap kinerja CPU, karena loop tersebut membutuhkan CPU cycle, sementara loop tersebut tidak menghasilkan apa-apa, jadi sama saja dengan menyia-nyiakan CPU
cycle.
Untuk mengatasi hal ini, dibuatlah modifikasi dari semafor, yaitu dengan menambahkan waiting
queue pada masing-masing semafor. Tujuannya adalah agar thread yang harus menunggu
dipindahkan ke waiting queue dan kegiatannya dihentikan sementara.
Ketika thread keluar dari critical section-nya, thread tersebut akan memanggil fungsibuka yang akan mengeluarkan satu (dari beberapa) thread yang berada dalam waiting queue lalu membangunkannya untuk kemudian memasuki critical section-nya.
Struktur semafor ini menjadi (masih dalam bahasa C):
void buka(int sem_value) {
sem_value++;
if(sem_value <= 0) {
/*keluarkan satu thread dari waiting queue*/ /*aktifkan thread tersebut*/
} }
void kunci(int sem_value) {
sem_value--; if(sem_value < 0) {
/*masukkan thread ke dalam waiting queue*/ /*blok thread tersebut*/
} }
Berbeda dengan semafor yang biasa, pada semafor yang telah dimodifikasi ini sem_value bisa menjadi negatif. Jika kita renungkan maknanya, ternyata ketika semafor bernilai negatif, nilai tersebut melambangkan banyaknya thread yang berada pada waiting queue semafor tersebut. Keuntungan menggunakan semafor:
1. dari segi programming, penanganan masalah sinkronisasi dengan semafor umumnya rapi dan teratur, sehingga mudah untuk dibuktikan kebenarannya
2. semafor diimplementasikan dalam hard code sehingga penggunaannya bersifat portabel.
20.5. Monitor
Monitor adalah suatu tipe data abstrak yang dapat mengatur aktivitas serta penggunaan resource oleh beberapa thread. Ide monitor pertama kali diperkenalkan oleh C.A.R Hoare dan Per Brinch-Hansen pada awal 1970-an.
Monitor terdiri atas data-data private dengan fungsi-fungsi public yang dapat mengakses data-data tersebut. Method-method dalam suatu monitor sudah dirancang sedemikian rupa agar hanya ada satu buah method yang dapat bekerja pada suatu saat. Hal ini bertujuan untuk menjaga agar semua operasi dalam monitor bersifat mutual exclusion.
Monitor dapat dianalogikan sebagai sebuah bangunan dengan tiga buah ruangan yaitu satu buah ruangan kontrol, satu buah ruang-tunggu-masuk, satu buah ruang-tunggu-dalam. Ketika suatu
thread memasuki monitor, ia memasuki ruang-tunggu-masuk (enter). Ketika gilirannya tiba, thread
memasuki ruang kontrol (acquire), di sini thread menyelesaikan tugasnya dengan shared resource yang berada di ruang kontrol (owning). Jika tugas thread tersebut belum selesai tetapi alokasi waktu untuknya sudah habis atau thread tersebut menunggu pekerjaan thread lain selesai, thread melepaskan kendali atas monitor (release) dan dipindahkan ke ruang-tunggu-dalam (waiting queue).
Ketika gilirannya tiba kembali, thread memasuki ruang kontrol lagi (acquire). Jika tugasnya selesai, ia keluar dari monitor (release and exit).