Threading .NET 2: Thread Pooling
Setelah kemarin gw membahas mengenai konsep multi-threading, gw sekarang akan switch on the code dan membahas multi-threading secara praktikal pada pengembangan program kita. Salah satu cara termudah menggunakan multi-threading adalah menggunakan thread pool. Thread pool adalah sistem yang melakukan manajemen thread untuk mengerjakan pekerjaan-pekerjaan yang ingin program kita lakukan di thread lain.
Thread pool melakukan manajemen thread secara efisien, sehingga kita tidak perlu melakukan atau membuat objek-objek thread secara manual setiap kita ingin melakukan suatu pekerjaan di thread lain. Thread pool cocok untuk program-program yang memiliki intensitas keluar-masuknya suatu pekerjaan ke dalam thread lain yang cukup banyak.
.NET Framework akan mengelola setiap thread secara efisien. Sehingga ketika program kita memasukkan pekerjaan baru ke dalam antrian thread pool, thread pool akan mencoba mengalokasikan thread untuk pekerjaan tersebut. Jika semua kursi thread sedang terisi, maka pekerjaan itu harus menunggu hingga ada thread kosong untuk mengerjakan pekerjaan tersebut.
Thread pool di .NET Framework menggunakan class ThreadPool. Class tersebut menyediakan method-method statik yang dapat digunakan untuk memanfaatkan thread pool yang disediakan oleh .NET Framework. Kita tidak bisa menginstantiasi class ThreadPool pada program kita, karena dalam satu program di .NET Framework hanya dapat memiliki satu thread pool saja.
Method yang digunakan untuk mendaftarkan suatu pekerjaan ke antrian thread pool adalah QueueUserWorkItem. Method tersebut menerima parameter berupa delegate (reference ke suatu method lain) WaitCallback di mana delegate tersebut merepresentasikan method yang akan dikerjakan oleh thread lain. Method tersebut harus memiliki satu parameter bertipe object dan tidak boleh memberikan nilai balikan apa-apa (return type bertipe void). Parameter dari method tersebut dapat digunakan untuk menyalurkan parameter apapun dari thread utama ke method tersebut yang dijalankan di thread lainnya. Berikut contoh sederhana method tersebut:
[sourcecode language=”csharp”] public static void HitungHitung(object obj){
// Tidur satu detik
Thread.Sleep(1000);
Console.WriteLine("Bangun tidur.");
}
[/sourcecode]
Method tersebut hanya tidur satu detik kemudian mencetak output ke layar. Berikut cara kita mendaftarkan method tersebut ke dalam antrian thread pool:
[sourcecode language=”csharp”] static void Main(string[] args){
for (int i = 0; i < 10; i++)
ThreadPool.QueueUserWorkItem(new WaitCallback(HitungHitung));
Console.WriteLine("Selesai ngantriin kerjaan.");
Console.ReadKey();
}
[/sourcecode]
Kadang kita ingin memberikan parameter pada method yang akan dikerjakan di thread lain tersebut. Kita dapat menggunakan parameter object dari method pekerjaan tersebut. Misalkan parameter yang akan kita berikan berupa integer, maka kita cast parameter obj tersebut ke tipe int. Kemudian pada saat kita mendaftarkan pekerjaan tersebut menggunakan method QueueUserWorkItem, kita masukkan parameter tersebut setelah parameter delegate.
[sourcecode language=”csharp”] public static void HitungHitung(object obj){
// Tidur satu detik
Thread.Sleep(1000);
int parameter = (int)obj;
Console.WriteLine("Bangun tidur di thread " + parameter + ".");
}
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
ThreadPool.QueueUserWorkItem(new WaitCallback(HitungHitung), i);
Console.WriteLine("Selesai ngantriin kerjaan.");
Console.ReadKey();
}
[/sourcecode]
Jangan lupa bahwa signature dari method pekerjaan tersebut harus memiliki tepat satu parameter bertipe object. Jangan membuat parameter yang bertipe bukan object, maupun menambah parameter-parameter lainnya di definisi method tersebut. Bagaimana jika kita ingin memberikan parameter yang kompleks, misalnya tiga buah nilai integer? Cukup buat class yang merepresentasikan parameter-parameter tersebut. Gampang kan? 😉
Satu teknis yang cukup signifikan adalah cara untuk mengetahui apakah method yang dikerjakan di thread lain tersebut sudah selesai dikerjakan atau belum. Jika kita menggunakan solusi thread pooling ini, thread utama pada umumnya tidak tahu-menahu kapan thread-thread pekerjaan itu selesai dieksekusi. Pada contoh program di atas, misalkan kita menekan satu tombol setelah keluaran “Selesai ngantriin kerjaan”, maka program akan langsung selesai walaupun thread-thread pekerjaan lainnya belum selesai dikerjakan.
Hal ini patut dihindari karena bisa saja thread pekerjaan tersebut melakukan sesuatu yang penting dan program tidak boleh berhenti sebelum thread pekerjaan itu selesai dieksekusi. Sehingga thread pekerjaan tersebut harus dengan suatu cara menginformasikan ke thread utama apakah pekerjaannya sudah selesai dilakukan atau belum.
Metode yang paling mudah adalah Signaling. Pada .NET Framework sudah disediakan class-class yang membantu proses signaling tersebut. Di sini kita akan menggunakan ManualResetEvent. ManualResetEvent adalah class untuk signaling yang sederhana, dimana dia cuma memiliki dua state: True dan False. Terdapat method Set pada class tersebut yang ketika dipanggil akan mengubah state objek ManualResetEvent tersebut ke fase True. True di sini menunjukkan bahwa pekerjaan telah selesai. Sehingga method Set tersebut harus dipanggil di akhir method yang melakukan pekerjaan tersebut.
Perhatikan program di bawah ini. Gw membuat satu kelas ParameterHitung yang mengenkapsulasi parameter yang diberikan ke method pekerjaan. Parameter-parameter yang diberikan adalah suatu nilai integer dan suatu objek dari ManualResetEvent:
[sourcecode language=”csharp”] class ParameterHitung{
public ManualResetEvent ResetEvent { get; set; }
public int ParameterInt { get; set; }
}
public static void HitungHitung(object obj)
{
// Tidur satu detik
Thread.Sleep(1000);
ParameterHitung parameter = (ParameterHitung)obj;
Console.WriteLine("Bangun tidur di thread " + parameter.ParameterInt + ".");
parameter.ResetEvent.Set();
}
static void Main(string[] args)
{
ManualResetEvent[] evtArr = new ManualResetEvent[10];
for (int i = 0; i < 10; i++)
{
evtArr[i] = new ManualResetEvent(false);
ParameterHitung param = new ParameterHitung();
param.ParameterInt = i;
param.ResetEvent = evtArr[i];
ThreadPool.QueueUserWorkItem(new WaitCallback(HitungHitung), param);
}
Console.WriteLine("Selesai ngantriin kerjaan.");
WaitHandle.WaitAll(evtArr);
Console.WriteLine("Selesai nungguin kerjaan.");
Console.ReadKey();
}
[/sourcecode]
Setiap satu pekerjaan yang didaftarkan di thread pool harus memiliki satu objek ManualResetEvent yang unik. Karena satu objek tersebut merepresentasikan tepat satu pekerjaan saja. Sehingga pada iterasi for di atas program menginstantiasi objek ManualResetEvent yang baru dan disimpan pada slot array yang berbeda.
Untuk menunggu semua pekerjaan selesai (i.e. semua signal sudah memberikan nilai True), kita dapat menggunakan method WaitAll dari class WaitHandle. Method tersebut adalah method statik yang menerima parameter berupa array WaitHandle (superclass dari ManualResetEvent) dan method tersebut menunggu setiap WaitHandle tersebut. Ketika semua signal sudah memberikan nilai True, method WaitAll akan selesai dieksekusi dan akan melanjutkan eksekusi program di thread utama, yaitu menyetak “Selesai nungguin kerjaan.”
Sekian dulu penjelasan tentang thread pool ini, yang mana teknik thread pooling ini adalah salah satu cara termudah mengimplementasikan multithreading di .NET Framework. Stay tuned for next posts 😉
misalkan saya membuat 8 object.
threadA, threadA1, threadB, threadB1, threadC, threadC1, threadD, threadD1.
karena punya quad core (ga sombong lho :D), maka saya menjalankan 4 thread secara bersama-sama (threadA, threadB, threadC, dan threadD).
masing2 menciptakan 1 worker thread, namun ketika menciptakan thread lainnya maka dirinya stop.
contoh: threadA berjalan kemudian menciptakan threadA1, ketika threadA1 berjalan maka threadA stop, ketika selesai menjalankan tugasnya si threadA1 stop kemudian threadA berjalan.
begitu juga dengan 3 thread lainnya.
yg mau saya tanyakan adalah, apakah 4 thread tersebut benar2 berjalan bersama atau hanya ada 1 thread yg berjalan namun karena pergantiaannya sangat cepat jadi seakan akan thread2 tersebut berjalan bersama? 🙂
Iya karena lo pakai quadcore kan. Jadinya 4 thread tersebut berjalan paralel di prosesor.
klo boleh minta solusi.
sekarang kasusnya gini.
misalkan 2 thread berjalan bersama.
threadA dan threadB.
kedua thread sama2 melakukan buka tutup koneksi DB karena melakukan manipulasi data.
pasti akan terjadi eror karena misalkan threadB sedang terkoneksi dengan DB sedangkan threadA tiba2 menutupnya.
klo kasusnya kayak gitu, baiknya gimana yah?
thanks before
Pakai dua adapter yang berbeda aja. Jadi koneksinya terpisah antar thread. Jangan pakai satu koneksi pada dua thread berbeda. Bisa berabe nanti.