WinForm: Cross-Thread

Soeleman 6 Januari 2017

WinForm: Cross-Thread

User-Experience(UX) adalah fitur dalam membuat sebuah aplikasi. Salah satunya dalam hal responsive dalam ber-interaksi dengan user-nya.

Ini merupakan kisah lama yang masih saja muncul di komunitas developer. Untuk WinForm, sudah banyak cara untuk mengatasi ini. Dalam tulisan ini kita coba lihat dengan cara paling sederhana sebagai contoh-nya.

Form

Kita buat sebuah Form dengan dua button dan picture-box. Seperti yang terlihat di bawah.

FormCrossThread Design

 

Cara kerjanya sederhana. Aplikasi ini akan men-download image dari internet ketika salah satu button di tekan. Setelah download-nya selesai, image itu akan di masukkan ke dalam picture-box. Pada saat meng-download image, posisi kedua button akan disable(menghindari multi-click).

FormCrossThread Run

 

Andaikata download image-nya sukses. Maka kita akan melihat gambar peta di picture-box dan setiap button di tekan. Image tersebut akan berganti-ganti.

FormCrossThread Done

 

Synchronous

Pada kode di bawah, kita hanya meng-aktif-kan satu tombol saja. Tombol itu adalah Get Image, tombol yang lain akan di pergunakan untuk cara yang lain.

Cara kerjanya seperti berikut. Tombol di tekan akan memanggil RunLongProcess yang di sana akan menggunakan GetImageMap. Kemudian hasil-nya akan di tampilkan oleh ShowImage.

RunLongProcess sendiri boleh di bilang sebagai persiapan sebelum GetImageMap. Terutama menangani multi-click lewat variable noProc.

GetImageMap adalah fungsi utama dan fungsi yang kita simulasikan sebagai fungsi yang menggunakan resouces yang besar. Di sini kita gunakan Thread.Sleep untuk membuat-nya lebih lama, di luar WebClient. Kadang meng-akses GoogleApi juga suka lambat.

Dan ShowImage merupakan fungsi untuk memasukkan hasil dari GetImageMap ke dalam picture-box dan reset button agar bisa aktif kembali.

VB.Net FormCrossThread Base - VB.Net C# FormCrossThread Base - C#

 

Dan kode di atas bisa menghasilkan hasil seperti yang di harapkan. Tapi ada sedikit yang kurang menyenangkan buat user.

 

Hang

Yang di maksud pengalaman kurang menyenangkan adalah pada saat kita menekan tombol Get Image maka Form akan tidak responsive. Ia tidak bisa di gerakkan sama sekali dan user akan berpikir aplikasi ini Hang. Seperti-nya ada sesuatu yang menghambat aplikasi ini untuk mengikuti perintah kita -- dalam hal ini menggerakkan posisi window-nya.

FormCrossThread Hang

 

Thread

Hal ini terjadi karena Thread yang di pakai oleh WinForm untuk menggambar window terhalang oleh kode yang menggunakan resouce(waktu atau cpu + memory). Bagaimanapun dalam sebuah Thread, hanya satu instruksi ke cpu dalam satu waktu. Biasanya tidak masalah karena cepat, hanya untuk kode tertentu yang seperti yang ada pada GetImageMap akan membuat winform tertunda melaksanakan tugasnya.

Salah satu cara paling umum di gunakan untuk mengatasi ini adalah menjalankan kode tersebut dalam Thread yang berbeda. Oleh karena itu ada perubahan yang di lakukan seperti di bawah.

Agar bisa kita melihat bedanya, maka sekarang kita gunakan button Get Image(Thread). Pada RunLongProcess ada perubahan sedikit agar bisa menjalankan dua cara. Penandanya adalah parameter nonThread.

Untuk memindahkan kode ke dalam Thread yang berbeda, cukup bungkus fungsi tersebut dengan class Thread. Pada saat method Start di jalankan maka fungsi GetImageMap akan di jalankan pada Thread yang kita buat ini. Mudah bukan?

VB.Net FormCrossThread Thread - VB.Net C# FormCrossThread Thread - C#

 

Error

Sayang seribu sayang, pada saat kita menekan tombol Get Image(Thread). Muncul error seperti di bawah.

VB.Net FormCrossThread Error - VB.Net C# FormCrossThread Error - C#

 

Apakah salah ku pada mu? Ya memang salah, karena fungsi GetImageMap akan memanggil fungsi ShowImage. Kita menjalankan GetImageMap dalam Thread yang berbeda dengan Thread Control (Winform) yang ada di fungsi ShowImage. Istilahnya sudah beda kamar tapi mau buka lemari di kamar yang lain, terhalang tembok kamar.

 

Invoke

Itu normal saja, karena aturan-nya seperti itu. Sesama Thread tidak boleh saling menerobos.

Dan aturannya juga memberikan cara untuk saling ber-komunikasi antar Thread. Dalam hal ini di lakukan dengan delegate, kalau di dunia lain biasa di kenal dengan nama pointer.

Jadi dengen delegate ini kita sampaikan data dari Thread yang satu ke Thread lain. Dalam kode ini, kita lemparkan data localFile ke Thread yang dimana Control berada.

Dan cara mudah saja kok. Pada fungsi GetImageMap kita gunakan Method Invoke dengan menggunakan delegate MethodInvoker.

VB.Net FormCrossThread Full - VB.Net C# FormCrossThread Full - C#

 

Penutup

Agar aplikasi kita tetap responsive ketika kita sedang melakukan sebuah pekerjaan yang memerlukan resource besar. Maka teknik yang di lakukan adalah menjalankan fungsi tersebut pada Thread yang berbeda.

Pada tulisan ini, kita melihat teknik dasar untuk itu. Umumnya cara ini sudah mulai di tinggalkan karena sekarang sudah banyak alternative untuk itu. Seperti dengan BackgroundWorker atau Async-Await akan lebih menyederhanakan kode kita untuk hal seperti ini.

 

Referensi

 
Perhatian! Code yang di tampilkan dalam tulisan ini merupakan ilustrasi dari yang ingin dipaparkan dan bukan production ready code. Sudah banyak kejadian karena asal meng-copy-and-paste tanpa mengerti code yang di ambil itu ke dalam production. Selain itu perlu ada tambahan code dan test sebelum siap untuk di gunakan secara utuh.