Hello Multithread
From GameDevID
author : uray
Contents |
Intro
Dalam multithreaded operating system seperti Unix family dan Windows, kita dapat menjalankan lebih dari satu program secara simultan walaupun prosesor yang digunakan hanya ada satu. Hal ini bisa terjadi karena operating system melakukan scheduling yang setiap proses (program yang sudah di memori) akan secara bergantian menggunakan resource komputer (CPU). Pergantian eksekusi antara program A dan program B tidak dirasakan oleh user karena pembagian waktu yang sangat pendek sehingga user merasa program berjalan secara paralel/bersamaan.
Dalam prosesor modern terutama prosesor multicore, dua thread dapat benar-benar dijalankan secara parallel. Hal ini disebabkan pada prosesor multicore terdapat dua buah prosesor single core yang dipadukan dalam satu kemasan. Tentu saja terdapat banyak desain multicore, ada yang benar-benar dua buah prosesor single core, ada yang masih men-share cache memory, ada yang bersifat simetris (core nya memiliki kemampuan komputasi yang sama), ada yang non-simetris (kemampuan komputasi antar core berbeda). Perbedaan ini menentukan seberapa efektif thread dieksekusi di masing - masing core untuk prosesor tersebut. Bahkan untuk core yang benar - benar tidak simetrik, multithreading bukanlah solusi yang tepat untuk menjalankan program parallel di prosesor tersebut.
Pada program yang bersifat multithreaded, kemampuan scheduling operating system ini dimanfaatkan untuk menjalankan lebih dari satu routine pada program tersebut secara paralel yaitu dengan membuat lebih dari satu thread untuk sebuah program. Thread dapat dianggap seperti halnya program yang bisa berjalan bersamaan Bedanya thread adalah bagian dari suatu program sehingga sesama thread dalam sebuah program dapat berbagi resource (misal:shared data).
Program multithreaded akan memiliki manfaat yang besar pada multiprosesor atau multicore processor karena program ini dapat menjalankan setiap routine pada setiap core dalam waktu yang bersamaan. Walaupun demikian manfaat ini tidak terbatas hanya pada multi processor/core saja. Program multithread akan sangat bermanfaat pada program yang banyak membaca I/O, grafik dan networking, sebagai contoh meng-update GUI, sambil membaca file dan melakukan pooling dari network dapat berjalan secara paralel walaupun pada single processor karena program mengakses tiga resource komputer yang berbeda.
Objective
Kode ini akan memberikan contoh bagaimana child thread dibuat dan dieksekusi menggunakan Win32 API. Dalam contoh ini akan dibuat dua thread yang berjalan secara paralel yaitu "child thread" dan "main thread". Kedua thread ini akan menambahkan nilai counter dan ditampilkan pada display.
Konsep
Setiap thread selalu memiliki (associated) thread procedure seperti layaknya "main function" dalam setiap program C/C++. "Main function" dianggap sebagai entry point (titik mula program) dan termination point (titik akhir program) yaitu program akan mulai melaksanakan instruksi yang dimulai dari baris pertama di "main function" dan program akan selesai ketika baris terakhir dari "Main function" dijalankan.
contoh main function dalam program C
1 int main( int argc,char* argv[] )
2 {
3 //........ program mulai setelah baris ini
4 int angka = 1;
5 printf("hello world!, angka = %d",angka);
6 //........ program berakhir setelah baris ini
7 }
Sifat "Main function" ini sama dengan sifat dari thread procedure. Perbedaan di antara keduanya adalah baris pertama dari thread procedure adalah titik mulai eksekusi thread dan baris terakhir thread procedure adalah titik akhir dari eksekusi thread. Setelah eksekusi program keluar dari thread procedure maka thread yang terhubung (associated) dengan thread procedure itu akan hilang.
Jika sebuah thread menciptakan thread baru (child thread) biasanya thread tersebut akan menunggu child thread sampai selesai sebelum thread tersebut berhenti. Hal ini disebut dengan join thread. Hal ini sangat penting karena main thread biasanya "memiliki" resource (yaitu bertanggung jawab mengalokasi dan dealokasi resource). Jika main thread keluar tanpa menunggu child thread dan child thread tersebut menggunakan resource yang dimiliki main thread maka akan terjadi error karena child thread mungkin masih menggunakan resource yang didealokasi oleh main thread. Error sepert ini mungkin sulit direproduksi karena tidak ada jaminan terdapat "relative timing" antara main thread dengan child thread.
Pengecualian terhadap kasus di atas adalah jika child thread tidak menggunakan resource yang dimiliki main thread atau child thread diharapkan memiliki lifetime yang jauh lebih kecil dari main thread (sehingga bisa dijamin child thread sudah selesai ketika main thread selesai). Untuk kasus ini dimungkinkan untuk tidak usah menggunakan thread yang harus di-join.
Operasi Join di Win32 menggunakan fungsi WaitForSingleObject atau WaitForMultipleObject. Penggunaan kedua fungsi tersebut tergantung pada banyaknya thread yang akan di join. Untuk POSIX thread, fungsi tersebut adalah thr_join().
Kebanyakan fungsi thread spawner (fungsi yang menciptakan child thread) akan menghasilkan thread handle. Handle ini adalah resource milik sistem operasi dan harus didealokasi. Secara spesifik handle yang diciptakan menggunakan CreateThread() harus didealokasikan dengan CloseHandle(). Thread yang diciptakan dengan thread spawner yang menghasilkan handle harus di-join karena thread handle adalah resource yang dialokasikan di main thread. Pada Win32 untuk menciptakan thread yang tidak usah di-join, anda dapat menggunakan fungsi _beginthread().
Untuk program yang banyak menggunakan static C-Runtime library, tidak disarankan untuk menggunakan fungsi CreateThread. CreateThread tidak menginisialisasi C runtime stack dengan benar, sehingga jika fungsi ExitThread dipanggil akan terjadi memory leak. Sebagai alternatif anda dapat menggunakan _beginthreadex. Fungsi ini aman digunakan dengan static C-Runtime library.Hal ini harus diperhatikan terutama jika anda menggunakan kompiler MinGW.
Example
Dalam contoh ini, kita akan menulis thread procedure sebagai berikut :
int childcounter = 0;
DWORD ThreadProc( LPVOID param )
{
//........ tambah nilai counter sampai sebuah tombol keyboard ditekan
while( !kbhit() )
{
childcounter++;
}
//........ sebuah tombol telah ditekan, child thread terminated.
return 0;
}
Contoh di bawah ini adalah cara membuat thread yang menggunakan thread procedure di atas :
int maincounter = 0;
void main(void)
{
HANDLE thread;
//........ buat child thread dan jalankan ThreadProc() pada thread ini
thread = CreateThread( NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc,NULL,0,NULL );
//........ routine program utama
while(true)
{
maincounter++;
printf("counter of main thread = %d , childthread = %d\n",maincounter,childcounter);
if( kbhit() && getch() == 'x' ) break;
}
WaitForSingleObject(thread,INFINITE);
CloseHandle(thread);
}
Dari contoh di atas bisa dicoba bahwa jika main thread (routine program utama) keluar yaitu ketika tombol x ditekan maka child thread ikut selesai. Apabila child thread selesai program utama tidak ikut selesai. Hal ini bisa dicoba dengan menekan sembarang tombol selain x maka child thread akan selesai tetapi program akan tetap jalan.

