Jumat, 06 Maret 2015

Mengenal PHP Thread

Mengenal dan Mempelajari Thread Pada PHP

PHP sudah bisa menjalankan thread sejak PHP 5.3. sebelumnya programmer mesti menggunakan proses forking di linux, namun tidak bisa jalan di Windows system. Namun sejak PHP 5.3 Thread mulai bisa diimplementasi dengan menggunakan komponen pthreads dari PECL

Thread sendiri merupakan sebuah metode untuk melakukan  proses secara bersamaan tanpa saling mengganggu satu sama lainnya, dan diatur oleh induknya. Analoginya seperti seorang bos tekstil mendistribusikan kerjaan design pola, potong bahan, penjahitan, dan lainnya secara paralel kepada masing-masing karyawannya tanpa harus saling menunggu.


Supaya bisa menjalankan thread, ada beberapa komponen yang mesti kita install :
  • php-zts
  • php pear
  • pthreads dari pecl
Namun untuk instalasi php-zts di CentOS agak sedikit masalah, karena CentOS tidak menyediakan rpm binary yang ready dipakai. Satu-satunya cara mesti install PHP dari source langsung. Jadi untuk mempersingkat, saya rangkum cara instalasi secara cepat saja.

Pertama Download PHP versi stabil dari website php.net pilih extension *.tar.gz, setelah berhasil download copy file tersebut ke /home/softwares/.
Kedua extract file php tersebut, misalnya nama filenya adalah php-5.6.6.tar.gz, maka jalankan perintah berikut init :
# tar -vzxf php-5.6.6.tar.gz
otomatis akan terbuat sebuah folder dengan nama php-5.6.6 yang berisi file instalasi php, masuk ke dalamnya dengan perintah :
# cd php-5.6.6
Sampai di sini kita coba instal dulu semua dependensi PHP lainnya yang kira-kira dipakai. pertama adalah harus meng-install repository EPEL silakan lihat cara instalasi repository epel di sini. dikarenakan epel repository itu berbeda untuk setiap instalasinya, saya hanya kasih contoh pada CentOS 7 saja, yang lainnya tolong dilihat di link sebelumnya.
## RHEL/CentOS 7 64-Bit ##
# wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm
# rpm -ivh epel-release-7-5.noarch.rpm
Setelah repository Epel terinstall, kita jalankan perintah ini untuk library pendukung php :
# yum install wget libxml2-devel httpd-devel libXpm-devel gmp-devel libicu-devel t1lib-devel aspell-devel openssl-devel bzip2-devel libcurl-devel libjpeg-devel libvpx-devel libpng-devel freetype-devel readline-devel libtidy-devel libxslt-devel libtidy-devel libmcrypt-devel mhash-devel
Setelah itu baru kita mulai install PHP dari sourcenya :
# cd /home/softwares/php-5.6.6
# cp php.ini-production  /usr/local/lib/php.ini
# ./configure --with-libdir=lib64 --prefix=/usr/local --with-layout=PHP --with-pear --with-apxs2 --enable-calendar --enable-bcmath --with-gmp --enable-exif --with-mcrypt --with-mhash --with-zlib --with-bz2 --enable-zip --enable-ftp --enable-mbstring --with-iconv --enable-intl --with-icu-dir=/usr --with-gettext --with-pspell --enable-sockets --with-openssl --with-curl --with-gd --enable-gd-native-ttf --with-jpeg-dir=/usr --with-png-dir=/usr --with-zlib-dir=/usr --with-xpm-dir=/usr --with-vpx-dir=/usr --with-freetype-dir=/usr --with-t1lib=/usr --with-libxml-dir=/usr --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --enable-soap --with-xmlrpc --with-xsl --with-tidy=/usr --with-readline --enable-pcntl --enable-sysvsem --enable-sysvshm --enable-sysvmsg --enable-shmop --enable-maintainer-zts
# make
# make install

Nah saat ini php sudah ter-install, sekarang kita akan install pthreads dari pecl. 
Jika command pecl tidak ditemukan, coba install dulu php-pear dengan perintah berikut ini :
# yum install php-pear.noarch
setelah pecl sudah terinstall, silakan jalankan perintah berikut ini untuk install pthreads :
# pecl install pthreads
Tunggu sampai terinstall. sekarang lanjut setting extension pthreads.so supaya bisa dijalankan oleh PHP. tambahkan baris ini ke bagian extension pada file /usr/local/lib/php.ini
extension=pthreads.so
Nah sekarang pthreads sudah terinstall, berarti sudah saat ini kita mencoba koding PHP dengan metode Thread Process. Pertama salin koding di bawah ini dan simpan sebagai file php dengan nama "baca_buku.php" dan jalankan di PHP CLI.
<?php
    class BacaBuku extends Thread {
        public $arg ;
        public $rangkuman ;
       
        public function __construct($arg) {
            $this->arg = $arg;
        }
       
        public function run() {
            if ($this->arg) {
                # Acak Perkiraan Waktu Membaca
                $sleep = mt_rand(5, 30);
                print "<".$this->arg['nama'] . "> Saya Mulai Baca Buku '". $this->arg['buku'] . "' Kira-Kira $sleep Detik\n" ;
                # Simulasi Lama Waktu Membaca
                sleep($sleep);
                print "<".$this->arg['nama'] . "> Selesai Baca Buku '". $this->arg['buku'] . "' Dalam Waktu $sleep Detik\n" ;
                $this->rangkuman = "Rangkuman " . $this->arg['nama'] . " Untuk Buku '" . $this->arg['buku'] . "' Dalam Waktu $sleep Detik. Tolong disimpan" ;
            }
        }
    }
   
    # Siapkan Antrian Buku Yang Mau Dibaca
    $AntrianBuku = array('Bobo','Donal Bebek','Doraemon','Kungfu Boy','Dragon Ball','Harry Potter','Hunger Games','Miki Tikus','Peter Pan','Dongeng') ;
   
    # Siapkan Data Pembaca
    $SemuaPembaca = array('Diana','Robin','Hendi') ;
    $RangkumanSemua = array() ;
    $Antrian = array() ;
    $prevText = "" ;
    while (true) {
        # Memberikan Pekerjaan Kepada Pembaca Jika Masih Ada Buku Yang Belum Dibaca
        foreach ($SemuaPembaca as $Pembaca) {
            if (!isset($Antrian[$Pembaca])) {
                if (count($AntrianBuku) > 0) {
                    $bukuSaatIni = array_shift($AntrianBuku) ;
                    $Antrian[$Pembaca] = new BacaBuku(array('nama' => $Pembaca, 'buku' => $bukuSaatIni)) ;
                    $Antrian[$Pembaca]->start() ;
                }
            }
        }
        if (count($AntrianBuku) <= 0 && count($Antrian) <= 0) {
            # Jika Buku Habis & Semua Pembaca Sudah Selesai Membaca Maka Keluar Dari Program
            break ;
        } else {
            # Informasi Status Saat Ini
            $arrLagiBaca = array() ;
            foreach ($Antrian as $Pembaca => $AntrianSaatIni) {
                # Jika Sudah Ada Pembaca Selesai Maka Antrian Dikosongkan Untuk Pembacaan Selanjutnya
                if (isset($Antrian[$Pembaca])) {
                    if ($Antrian[$Pembaca]->isRunning() === false) {
                        # Jika Pembaca Sudah Selesai Baca Buku, Maka Antrian Dihapus
                        if ($Antrian[$Pembaca]->join()) {
                            $RangkumanSemua[] = $Antrian[$Pembaca]->rangkuman ;
                            unset($Antrian[$Pembaca]) ;
                        }
                    }
                }
                if (isset($Antrian[$Pembaca])) {
                    $arrLagiBaca[] = $Pembaca . " Saat Ini Sedang Baca '" . $Antrian[$Pembaca]->arg['buku'] . "'";
                }
            }
            if (count($arrLagiBaca) > 0) {
                if (count($AntrianBuku) > 0) {
                    $text = "\nMasih Ada ".count($AntrianBuku)." Belum Dibaca Dan Pembaca :\n - " . implode("\n - ", $arrLagiBaca) . "\n\n" ;
                } else {
                    $text = "\nAntrian Buku Sudah Habis Dan Pembaca :\n - " . implode("\n - ", $arrLagiBaca) . "\n\n" ;
                }
            } else {
                if (count($AntrianBuku) > 0) {
                    $text = "\nMasih Ada ".count($AntrianBuku)." Belum Dibaca\n\n" ;
                } else {
                    $text = "\nAntrian Buku Sudah Habis\n\n" ;
                }
            }
            if ($text != $prevText) {
                print "$text" ;
            }
            $prevText = $text ;
        }
        usleep(1000) ;
    }
   
    # Cetak Hasil Rangkuman Masing-Masing Pembaca
    foreach ($RangkumanSemua as $Rangkuman) {
        print "-> $Rangkuman\n" ;
    }
?>
Setelah dijalankan di PHP CLI, tampilannya kurang lebih akan seperti ini :
























Penjelasan programnya adalah saya membuat sebuah proses antrian untuk membaca 10 buku oleh 3 orang pembaca, namun siapa yang sudah selesai boleh langsung lanjut membaca buku selanjut tanpa harus menunggu yang lainnya selesai terlebih dahulu.

Jadi awalnya saya menyiapkan data 10 buku dan 3 pembaca :
$AntrianBuku = array('Bobo','Donal Bebek','Doraemon','Kungfu Boy','Dragon Ball','Harry Potter','Hunger Games','Miki Tikus','Peter Pan','Dongeng') ;
$SemuaPembaca = array('Diana','Robin','Hendi') ;
Kemudian melalui perulangan yang tidak akan berhenti kecuali semua buku telah terbaca, maka thread induk setiap saat akan melakukan pengecekan ke pembaca yang membaca. Jika sedang tidak membaca dan buku masih ada, makan akan di-assign untuk membaca buku.
if (!isset($Antrian[$Pembaca])) {
    if (count($AntrianBuku) > 0) {
        $bukuSaatIni = array_shift($AntrianBuku) ;
        $Antrian[$Pembaca] = new BacaBuku(array('nama' => $Pembaca, 'buku' => $bukuSaatIni)) ;
        $Antrian[$Pembaca]->start() ;
    }
}
Di sini, Kita membuat sebuah thread baru dan langsung menyuruhnya membaca dengan fungsi Thread->start().

Sembari mengecek apakah sudah ada pembaca yang sudah selesai baca, dan disimpan hasil rangkumannya dengan koding di bawah ini :
foreach ($Antrian as $Pembaca => $AntrianSaatIni) {
    if (isset($Antrian[$Pembaca])) {
        if ($Antrian[$Pembaca]->isRunning() === false) {
            if ($Antrian[$Pembaca]->join()) {
                $RangkumanSemua[] = $Antrian[$Pembaca]->rangkuman ;
                unset($Antrian[$Pembaca]) ;
            }
        }
    }
}
Fungsi Thread->isRunning() di sini dimaksudkan untuk mengecek apakah fungsi Thread->run() di thread sudah selesai dijalankan atau belum, jika sudah selesai, maka thread tersebut akan digabungkan ke induk dengan fungsi Thread->join(), sehingga thread Induk bisa membaca data rangkuman dari Thread tersebut.

Informasi mengenai PHP Thread sendiri bisa dilihat di sini namun memang untuk PHP Thread ini dokumentasinya masih sangat sedikit sekali, dikarenakan Thread sendiri tidak populer di web programming, karena threading sendiri sudah di-handle sama web server seperti Apache.

File contoh bisa di download di sini

1 komentar: