Multithreading Dengan Mudah Di Griffon


Hampir semua jenis GUI memakai sebuah thread tunggal untuk menangani seluruh operasi yang berkaitan dengan GUI tersebut. Ingin buktinya? Drag sebuah button ke form, lalu buat sebuah kode program yang memakan waktu lama (mungkin 3 menit!).  Jalankan program dan klik button tersebut!  Hasilnya?  Program akan terlihat seolah-olah hang dan tidak dapat merespon.   Mungkin itu sebabnya saya pernah membaca cerita developer berpengalaman yang protes melihat tutorial memberi aksi pada button yang sesederhana ini tanpa menyebutkan soal thread sama sekali.

Btw, masalah ini bukan hanya terjadi pada Swing milik Java, tapi juga Windows Presentation Foundation milik .NET, GTK, SWT, dan banyak lagi.  Karena yang menangani tampilan hanya sebuah thread, maka bila ingin men-update tampilan, harus berada di thread tersebut.   Secara teori, melakukan modifikasi tampilan tanpa melalui thread yang mengelola tampilan, adalah sebuah kesalahan!

Thread yang mengelola tampilan di Swing disebut sebagai Event Dispatching Thread (EDT).  Semua kode program yang saya buat untuk memodifikasi user interface (misalnya, mengubah isi label, mengubah warna, dsb) harus berjalan pada EDT.   Selain itu, semua kode program yang membutuhkan waktu eksekusi yang lama harus berjalan pada sebuah thread lain.   Jika dipaksakan berjalan pada EDT, maka selama EDT sibuk mengerjakan proses tersebut, tampilan tidak akan memberi respon pada pengguna dan terlihat seperti nge-hang.

Beruntungnya, Griffon sebagai framework MVC untuk aplikasi desktop, dilengkapi dengan berbagai kemudahan dalam pengelolaan threading.   Salah satunya adalah kode program untuk view secara otomatis akan dijalankan di EDT, sementara itu kode program untuk controller secara otomatis akan dijalankan pada thread terpisah.   Developer tentu saja masih punya kewenangan untuk mengubah perilaku ini.

Sebagai contoh, saya ingin men-simulasi-kan cara yang SALAH, yaitu menjalankan kode program controller yang memakan waktu lama di EDT. Saya mulai dengan membuat view sederhana seperti berikut ini:

package latihan

application(title: 'latihan',
  preferredSize: [520, 240],
  pack: true,
  locationByPlatform:true,
  iconImage: imageIcon('/griffon-icon-48x48.png').image,
  iconImages: [imageIcon('/griffon-icon-48x48.png').image,
               imageIcon('/griffon-icon-32x32.png').image,
               imageIcon('/griffon-icon-16x16.png').image]) {

	flowLayout()
	button("Simulasi Proses Lama", 
		actionPerformed: controller.simulasiProsesLama)
	textField(id: "txtOutput", columns: 20)
	button("Klik Saya Saat Simulasi Proses Lama Berlangsung",
		actionPerformed: controller.klikSelamaSimulasi)
}

Lalu pada controller, saya membuat kode program seperti berikut ini:

package latihan

import griffon.transform.Threading

class LatihanController {

	def view

	@Threading(Threading.Policy.INSIDE_UITHREAD_SYNC)
	def simulasiProsesLama = {
		// Looping tak terhingga
		while (true)
			print "Looping & Looping terus..."		
	}

	def klikSelamaSimulasi = {
		view.txtOutput.text = "Nilai dikirim ke text field!"
	}
}

Perhatikan bahwa saya membuat looping tak terhingga di closure simulasiProsesLama.   Anggap saja ini mewakili sebuah proses yang memakan waktu lama, misalnya melakukan query yang sangat berat atau mencari file di harddisk.   Kemudian, saya memaksakan agar method ini dikerjakan di EDT dengan memberikan annotation @Threading.    Bila saya men-klik tombol “Simulasi Proses Lama“, maka hasilnya akan terlihat seperti pada gambar berikut ini:

Tampilan UI yang Hang

Tampilan UI yang Hang

Begitu saya men-klik tombol tersebut, maka aplikasi menjadi hang!   Yup!   Inilah yang terjadi bila saya ‘sembarangan‘ meletakkan kode program.

Griffon sebenarnya sudah secara otomatis menjalankan kode program controller di thread terpisah.   Saya hanya perlu membuang annotation @Threading di closure simulasiProsesLama untuk membuat aplikasi berjalan sesuai dengan yang diharapkan.

Bagaimana bila saya ingin men-update tampilan GUI di controller? Bukankah untuk melakukan perubahan terhadap GUI harus selalui di EDT, sementara kode program controller berjalan di thread terpisah? Saya bisa meletakkan kode program yang melakukan modifikasi GUI di sebuah closure yang dilewatkan sebagai argumen untuk method edt().

Sebagai contoh, saya mengubah kode program pada controller untuk mengubah nilai JTextField selama berada dalam looping tak terhingga, yang terlihat seperti berikut ini:

package latihan

import java.awt.Color

class LatihanController {

	def view

	def rand = new Random()

	def simulasiProsesLama = {
		def i = 0;
		// Looping tak terhingga
		while (true) {
			edt {
				view.txtOutput.text = i++
			}
		}

	}

	def klikSelamaSimulasi = {
                edt {
		  view.txtOutput.setBackground(new Color(
			rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)))
                }
	}
}

Hasilnya bila dijalankan akan terlihat seperti pada animasi GIF berikut ini:

Tetap Responsif Saat Mengupdate UI

Tetap Responsif Saat Mengupdate UI

Apa yang terjadi bila saya lupa memanggil method edt()? Tampilan angka tidak akan terlihat bertambah satu per satu secara mulus, tetapi kadang loncat cukup jauh. Ini bukan masalah logic di program karena saya sudah benar memakai i++, hanya EDT yang ‘tidak pasti‘ memperbaharui tampilan.   Yup!   Salah satu bug yang paling menakutkan adalah bug yang berhubungan multithreading.   Bug jenis ini sangat susah ditebak dan sulit direka ulang.

Perihal Solid Snake
I'm nothing...

One Response to Multithreading Dengan Mudah Di Griffon

  1. Ping-balik: Melakukan Pengujian Java Swing Dengan FEST « The Solid Snake

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: