Multi-threading di php 7

Saat Anda perlu mempercepat aplikasi, tetapi menurut Anda semuanya telah dioptimalkan, Anda dapat meningkatkan kinerja dengan membagi pemrosesan data. Misalnya, jika Anda memiliki file dengan 1000 baris dan Anda ingin membacanya lebih cepat

Multi-threading di php 7

Jadi, Anda dapat membuat, misalnya, 10 node yang akan mengambil potongannya sendiri, membaca datanya, dan mengerjakan sisanya. Dalam hal ini, Anda akan memproses file 10 kali lebih cepat

Multi-threading di php 7

Perbedaan antara Multiprocessing dan Multithreading

Perbedaannya adalah utas berjalan di ruang memori yang sama, sedangkan proses memiliki memori terpisah. Ini membuatnya sedikit lebih sulit untuk berbagi objek antar proses dengan multiprosesing

Proses pemijahan sedikit lebih lambat daripada pemijahan utas. Begitu mereka berjalan, tidak ada banyak perbedaan

Aplikasi PHP multithreading (PThreads)

PThreads (v3) hanya dapat digunakan dengan PHP 7. 2+. Ini karena mode ZTS tidak aman di 7. 0 dan 7. 1

Untuk menggunakan PThreads, Anda memerlukan build PHP dengan ZTS (Zend Thread Safety) diaktifkan ( --enable-maintainer-zts atau --enable-zts di Windows ). Itu artinya Anda harus membangun (atau membangun kembali) PHP Anda. Proses ini dapat memakan waktu sekitar 30 menit (tergantung perangkat keras tempat Anda membuatnya). Selain itu, jika Anda memiliki ekstensi tambahan (terutama ekstensi PECL), ekstensi tersebut mungkin tidak berfungsi (atau bahkan dikompilasi) dengan benar

Mempertimbangkan kelemahan ini, sebagian besar proyek memutuskan untuk menghindari PThread sama sekali

Aplikasi multiproses PHP (PCNTL)

Ini adalah metode paling dasar dalam banyak proyek sederhana. Kami akan memotong proses PHP kami menggunakan pcntl_fork, dan melakukan beberapa operasi secara terpisah untuk setiap proses

Saat pcntl_fork dipanggil, salinan proses utama telah dibuat. Proses utama (induk) akan terus mengeksekusi kode di dalam blok di mana $pid > 0 dan tidak akan pernah tahu apa yang terjadi di blok anak. Jadi, kedua proses dibuat dari satu titik, tetapi menjadi terisolasi. Mereka tidak memiliki memori bersama

Terkadang kami memiliki proses anak yang membutuhkan waktu eksekusi lebih lama daripada proses orang tua. Kemudian kita dapat menggunakan fungsi khusus pcntl_wait untuk menunggu semua proses anak selesai

Pertukaran data lintas proses

Proses forking sederhana. Tapi menyusun mereka bersama tidak mungkin. Dan bagaimana jika kita perlu menerima data dari anak (atau bahkan beberapa anak proses)?

Nah, implementasinya bisa didasarkan pada beberapa pendekatan. memori bersama, file sementara, pemipaan proses, dll. Namun jika kami membutuhkan perpesanan instan, kami akan menggunakan stream_socket_pair

Multi-threading di php 7

Kami punya pertanyaan lain. bagaimana cara memanggil prosedur membaca untuk menghentikan proses utama dan mendapatkan data dari seorang anak?

Untuk tujuan ini, kita dapat menggunakan pcntl_signal dan posix_kill

pcntl_signal akan menangani sinyal yang dikirim ke proses kami dari luar dan posix_kill akan mengirimkan sinyal ke proses induk yang akan memulai proses untuk membaca data dari anak

Artikel ini ditinjau oleh rekan sejawat oleh Christopher Pitt. Terima kasih kepada semua peninjau sejawat SitePoint karena membuat konten SitePoint sebaik mungkin


Pengembang PHP tampaknya jarang memanfaatkan paralelisme. Daya tarik kesederhanaan pemrograman sinkron, single-threaded tentu saja tinggi, tetapi terkadang penggunaan sedikit konkurensi dapat membawa beberapa peningkatan kinerja yang bermanfaat.

Pada artikel ini, kita akan melihat bagaimana threading dapat dilakukan di PHP dengan ekstensi pthreads. Ini akan membutuhkan versi ZTS (Zend Thread Safety) dari PHP 7. x diinstal, bersama dengan pthreads v3 diinstal. (Pada saat penulisan, PHP 7. 1 pengguna perlu menginstal dari cabang master repo pthreads – lihat detail tentang membangun ekstensi pihak ketiga dari sumber. )

Hanya sebagai klarifikasi cepat. pthreads v2 menargetkan PHP 5. x dan tidak lagi didukung; . x dan sedang dikembangkan secara aktif

Multi-threading di php 7

Terima kasih banyak kepada Joe Watkins (pencipta ekstensi pthreads) untuk mengoreksi dan membantu meningkatkan artikel saya

Kapan tidak menggunakan pthreads

Sebelum kita melanjutkan, pertama-tama saya ingin mengklarifikasi kapan Anda tidak boleh (dan juga tidak bisa) menggunakan ekstensi pthreads

Di pthreads v2, rekomendasinya adalah bahwa pthreads tidak boleh digunakan di lingkungan server web (mis. e. dalam proses FCGI). Pada pthreads v3, rekomendasi ini telah diterapkan, jadi sekarang Anda tidak dapat menggunakannya di lingkungan server web. Dua alasan utama untuk ini adalah

  1. Tidak aman menggunakan banyak utas dalam lingkungan seperti itu (menyebabkan masalah IO, di antara masalah lainnya)
  2. Itu tidak berskala dengan baik. Misalnya, katakanlah Anda memiliki skrip PHP yang membuat utas baru untuk menangani beberapa pekerjaan, dan skrip tersebut dijalankan pada setiap permintaan. Ini berarti bahwa untuk setiap permintaan, aplikasi Anda akan membuat satu utas baru (ini adalah 1. 1 model threading – satu utas ke satu permintaan). Jika aplikasi Anda melayani 1.000 permintaan per detik, maka itu membuat 1.000 utas per detik. Memiliki banyak utas yang berjalan pada satu mesin akan dengan cepat membanjirinya, dan masalahnya hanya akan diperparah dengan meningkatnya tingkat permintaan.

Itu sebabnya threading bukanlah solusi yang baik di lingkungan seperti itu. Jika Anda mencari threading sebagai solusi untuk tugas pemblokiran IO (seperti melakukan permintaan HTTP), izinkan saya mengarahkan Anda ke arah pemrograman asinkron, yang dapat dicapai melalui kerangka kerja seperti Amp. SitePoint telah merilis beberapa artikel bagus yang mencakup topik ini (seperti menulis perpustakaan asinkron dan Modding Minecraft di PHP), jika Anda tertarik

Dengan itu, mari kita langsung ke hal-hal

Menangani tugas satu kali

Terkadang, Anda ingin menangani tugas satu kali dengan cara multi-utas (seperti melakukan beberapa tugas yang terikat IO). Dalam kasus seperti itu, kelas

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
2 dapat digunakan untuk membuat utas baru dan menjalankan beberapa unit kerja di utas terpisah itu

Sebagai contoh

$task = new class extends Thread {
    private $response;

    public function run()
    {
        $content = file_get_contents("http://google.com");
        preg_match("~<title>(.+)</title>~", $content, $matches);
        $this->response = $matches[1];
    }
};

$task->start() && $task->join();

var_dump($task->response); // string(6) "Google"
_

Di atas, metode

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
_3 adalah unit kerja kami yang akan dieksekusi di dalam utas baru. Saat memanggil
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
4, utas baru muncul dan metode
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
3 dipanggil. Kami kemudian menggabungkan utas yang dihasilkan kembali ke utas utama (melalui
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
6), yang akan memblokir hingga utas terpisah selesai dieksekusi. Ini memastikan bahwa tugas telah selesai dieksekusi sebelum kami mencoba menampilkan hasilnya (disimpan di
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
7)

Mungkin tidak diinginkan untuk mencemari tanggung jawab kelas dengan logika terkait utas (termasuk harus mendefinisikan metode

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
3). Kami dapat memisahkan kelas-kelas tersebut dengan meminta mereka memperluas kelas
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
9 sebagai gantinya, di mana mereka kemudian dapat dijalankan di dalam utas lainnya

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);

Kelas apa pun yang perlu dijalankan di dalam utas terpisah harus memperluas kelas

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
9 dengan cara tertentu. Ini karena memberikan kemampuan yang diperlukan untuk berjalan di dalam utas yang berbeda, serta memberikan keamanan implisit dan antarmuka yang berguna (untuk hal-hal seperti sinkronisasi sumber daya)

Mari kita lihat sekilas hierarki kelas yang diekspos oleh pthreads

Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool

Kita telah melihat dan mempelajari dasar-dasar tentang kelas

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
2 dan
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
9, jadi sekarang mari kita lihat tiga sisanya (
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
3,
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
4, dan
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
5)

Benang daur ulang

Memutar utas baru untuk setiap tugas yang akan diparalelkan itu mahal. Ini karena arsitektur shared-nothing harus digunakan oleh pthreads untuk mencapai threading di dalam PHP. Artinya, seluruh konteks eksekusi dari instance interpreter PHP saat ini (termasuk setiap kelas, antarmuka, sifat, dan fungsi) harus disalin untuk setiap utas yang dibuat. Karena ini menimbulkan dampak kinerja yang nyata, utas harus selalu digunakan kembali jika memungkinkan. Utas dapat digunakan kembali dengan dua cara. dengan

Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
_3s atau dengan
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
5s

Kelas

Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
_3 digunakan untuk menjalankan serangkaian tugas secara sinkron di dalam utas lain. Ini dilakukan dengan membuat instance
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
_3 baru (yang membuat utas baru), lalu menumpuk tugas ke utas terpisah tersebut (melalui
class Task extends Threaded
{
    private $value;

    public function __construct(int $i)
    {
        $this->value = $i;
    }

    public function run()
    {
        usleep(250000);
        echo "Task: {$this->value}\n";
    }
}

$worker = new Worker();
$worker->start();

for ($i = 0; $i < 15; ++$i) {
    $worker->stack(new Task($i));
}

while ($worker->collect());

$worker->shutdown();
0)

Ini contoh singkatnya

class Task extends Threaded
{
    private $value;

    public function __construct(int $i)
    {
        $this->value = $i;
    }

    public function run()
    {
        usleep(250000);
        echo "Task: {$this->value}\n";
    }
}

$worker = new Worker();
$worker->start();

for ($i = 0; $i < 15; ++$i) {
    $worker->stack(new Task($i));
}

while ($worker->collect());

$worker->shutdown();

Keluaran

Multi-threading di php 7

Di atas menumpuk 15 tugas ke objek

class Task extends Threaded
{
    private $value;

    public function __construct(int $i)
    {
        $this->value = $i;
    }

    public function run()
    {
        usleep(250000);
        echo "Task: {$this->value}\n";
    }
}

$worker = new Worker();
$worker->start();

for ($i = 0; $i < 15; ++$i) {
    $worker->stack(new Task($i));
}

while ($worker->collect());

$worker->shutdown();
1 baru melalui
class Task extends Threaded
{
    private $value;

    public function __construct(int $i)
    {
        $this->value = $i;
    }

    public function run()
    {
        usleep(250000);
        echo "Task: {$this->value}\n";
    }
}

$worker = new Worker();
$worker->start();

for ($i = 0; $i < 15; ++$i) {
    $worker->stack(new Task($i));
}

while ($worker->collect());

$worker->shutdown();
0, dan kemudian memprosesnya dalam urutan bertumpuk. Metode
class Task extends Threaded
{
    private $value;

    public function __construct(int $i)
    {
        $this->value = $i;
    }

    public function run()
    {
        usleep(250000);
        echo "Task: {$this->value}\n";
    }
}

$worker = new Worker();
$worker->start();

for ($i = 0; $i < 15; ++$i) {
    $worker->stack(new Task($i));
}

while ($worker->collect());

$worker->shutdown();
_3, seperti yang terlihat di atas, digunakan untuk membersihkan tugas setelah selesai dijalankan. Dengan menggunakannya di dalam while loop, kami memblokir utas utama sampai semua tugas yang ditumpuk selesai dieksekusi dan telah dibersihkan sebelum kami memicu
class Task extends Threaded
{
    private $value;

    public function __construct(int $i)
    {
        $this->value = $i;
    }

    public function run()
    {
        usleep(250000);
        echo "Task: {$this->value}\n";
    }
}

$worker = new Worker();
$worker->start();

for ($i = 0; $i < 15; ++$i) {
    $worker->stack(new Task($i));
}

while ($worker->collect());

$worker->shutdown();
4. Mematikan pekerja sebelum waktunya (mis. e. sementara masih ada tugas yang harus dijalankan) masih akan memblokir utas utama sampai semua tugas selesai dijalankan - tugas tidak akan dikumpulkan dari sampah (menyebabkan kebocoran memori)

Kelas

Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
_3 menyediakan beberapa metode lain yang berkaitan dengan tumpukan tugasnya, termasuk
class Task extends Threaded
{
    private $value;

    public function __construct(int $i)
    {
        $this->value = $i;
    }

    public function run()
    {
        usleep(250000);
        echo "Task: {$this->value}\n";
    }
}

$worker = new Worker();
$worker->start();

for ($i = 0; $i < 15; ++$i) {
    $worker->stack(new Task($i));
}

while ($worker->collect());

$worker->shutdown();
6 untuk menghapus tumpukan item terlama, dan
class Task extends Threaded
{
    private $value;

    public function __construct(int $i)
    {
        $this->value = $i;
    }

    public function run()
    {
        usleep(250000);
        echo "Task: {$this->value}\n";
    }
}

$worker = new Worker();
$worker->start();

for ($i = 0; $i < 15; ++$i) {
    $worker->stack(new Task($i));
}

while ($worker->collect());

$worker->shutdown();
7 untuk jumlah item pada tumpukan eksekusi. Tumpukan pekerja hanya menampung tugas yang akan dieksekusi. Setelah tugas di tumpukan dieksekusi, tugas itu dihapus dan kemudian ditempatkan di tumpukan (internal) terpisah untuk dikumpulkan sampah (menggunakan
class Task extends Threaded
{
    private $value;

    public function __construct(int $i)
    {
        $this->value = $i;
    }

    public function run()
    {
        usleep(250000);
        echo "Task: {$this->value}\n";
    }
}

$worker = new Worker();
$worker->start();

for ($i = 0; $i < 15; ++$i) {
    $worker->stack(new Task($i));
}

while ($worker->collect());

$worker->shutdown();
3)

Cara lain untuk menggunakan kembali utas saat menjalankan banyak tugas adalah dengan menggunakan kumpulan utas (melalui kelas

Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
5). Kumpulan utas diberdayakan oleh sekelompok
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
3 untuk memungkinkan tugas dieksekusi secara bersamaan, di mana faktor konkurensi (jumlah utas tempat kumpulan berjalan) ditentukan saat pembuatan kumpulan

Mari sesuaikan contoh di atas untuk menggunakan kumpulan pekerja sebagai gantinya

class Task extends Threaded
{
    private $value;

    public function __construct(int $i)
    {
        $this->value = $i;
    }

    public function run()
    {
        usleep(250000);
        echo "Task: {$this->value}\n";
    }
}

$pool = new Pool(4);

for ($i = 0; $i < 15; ++$i) {
    $pool->submit(new Task($i));
}

while ($pool->collect());

$pool->shutdown();

Keluaran

Multi-threading di php 7

Ada beberapa perbedaan penting antara menggunakan kumpulan dibandingkan dengan pekerja. Pertama, kumpulan tidak perlu dimulai secara manual, mereka mulai menjalankan tugas segera setelah tersedia. Kedua, kami mengirimkan tugas ke kumpulan, daripada menumpuknya. Juga, kelas

Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
5 tidak memperpanjang
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
9, sehingga tidak dapat diteruskan ke utas lainnya (tidak seperti
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
3)

Sebagai praktik yang baik, pekerja dan kumpulan harus selalu mengumpulkan tugas mereka setelah selesai, dan ditutup secara manual. Utas yang dibuat melalui kelas

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
_2 juga harus digabungkan kembali ke utas pembuat

pthreads dan (im) mutabilitas

Kelas terakhir yang akan dibahas adalah

Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
4 – tambahan baru untuk pthreads v3. Kekekalan telah menjadi konsep penting dalam pthreads, karena tanpanya, kinerja akan sangat menurun. Oleh karena itu, secara default, properti kelas
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
9 yang merupakan objek
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
9 itu sendiri sekarang tidak dapat diubah, sehingga tidak dapat dipindahkan setelah penugasan awal. Mutabilitas eksplisit untuk properti seperti itu sekarang disukai, dan masih bisa dilakukan dengan menggunakan kelas
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
4 yang baru

Mari kita lihat sekilas contoh untuk mendemonstrasikan batasan kekekalan yang baru

class Task extends Threaded // a Threaded class
{
    public function __construct()
    {
        $this->data = new Threaded();
        // $this->data is not overwritable, since it is a Threaded property of a Threaded class
    }
}

$task = new class(new Task()) extends Thread { // a Threaded class, since Thread extends Threaded
    public function __construct($tm)
    {
        $this->threadedMember = $tm;
        var_dump($this->threadedMember->data); // object(Threaded)#3 (0) {}
        $this->threadedMember = new StdClass(); // invalid, since the property is a Threaded member of a Threaded class
    }
};

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
_9 properti dari
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
4 kelas, di sisi lain, dapat berubah

class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};

Kita dapat melihat bahwa kelas

Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
_4 mengesampingkan kekekalan yang diberlakukan oleh kelas induknya
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
9 untuk memungkinkan
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
9 properti dapat ditetapkan kembali (serta
class Task extends Threaded // a Threaded class
{
    public function __construct()
    {
        $this->data = new Threaded();
        // $this->data is not overwritable, since it is a Threaded property of a Threaded class
    }
}

$task = new class(new Task()) extends Thread { // a Threaded class, since Thread extends Threaded
    public function __construct($tm)
    {
        $this->threadedMember = $tm;
        var_dump($this->threadedMember->data); // object(Threaded)#3 (0) {}
        $this->threadedMember = new StdClass(); // invalid, since the property is a Threaded member of a Threaded class
    }
};
4)

Hanya ada satu topik mendasar terakhir yang harus dibahas sehubungan dengan mutabilitas dan kelas

Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
4 - array. Array di pthreads secara otomatis dipaksa ke
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
4 objek saat ditugaskan ke properti kelas
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
9. Ini karena tidak aman untuk memanipulasi array dari berbagai konteks di PHP

Mari kita sekali lagi melihat contoh untuk lebih memahami berbagai hal

$array = [1,2,3];

$task = new class($array) extends Thread {
    private $data;

    public function __construct(array $array)
    {
        $this->data = $array;
    }

    public function run()
    {
        $this->data[3] = 4;
        $this->data[] = 5;

        print_r($this->data);
    }
};

$task->start() && $task->join();

/* Output:
Volatile Object
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)
*/

Kita dapat melihat bahwa

Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
4 objek dapat diperlakukan seolah-olah mereka adalah array, karena mereka memberikan dukungan untuk operasi berbasis array (seperti yang ditunjukkan di atas) dengan operator subset (
class Task extends Threaded // a Threaded class
{
    public function __construct()
    {
        $this->data = new Threaded();
        // $this->data is not overwritable, since it is a Threaded property of a Threaded class
    }
}

$task = new class(new Task()) extends Thread { // a Threaded class, since Thread extends Threaded
    public function __construct($tm)
    {
        $this->threadedMember = $tm;
        var_dump($this->threadedMember->data); // object(Threaded)#3 (0) {}
        $this->threadedMember = new StdClass(); // invalid, since the property is a Threaded member of a Threaded class
    }
};
9).
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
4 kelas tidak, bagaimanapun, didukung oleh fungsi berbasis array umum, seperti
class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
1 dan
class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
2. Sebagai gantinya, kelas
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
_9 memberi kita operasi seperti metode bawaan

Sebagai demonstrasi

$data = new class extends Volatile {
    public $a = 1;
    public $b = 2;
    public $c = 3;
};

var_dump($data);
var_dump($data->pop());
var_dump($data->shift());
var_dump($data);

/* Output:
object(class@anonymous)#1 (3) {
  ["a"]=> int(1)
  ["b"]=> int(2)
  ["c"]=> int(3)
}
int(3)
int(1)
object(class@anonymous)#1 (1) {
  ["b"]=> int(2)
}
*/

Operasi lain yang didukung meliputi

class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
_4 dan
class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
5

Sinkronisasi

Topik terakhir yang akan kita bahas dalam artikel ini adalah sinkronisasi dalam pthreads. Sinkronisasi adalah teknik untuk memungkinkan akses terkontrol ke sumber daya bersama

Sebagai contoh, mari terapkan penghitung naif

$counter = new class extends Thread {
    public $i = 0;

    public function run()
    {
        for ($i = 0; $i < 10; ++$i) {
            ++$this->i;
        }
    }
};

$counter->start();

for ($i = 0; $i < 10; ++$i) {
    ++$counter->i;
}

$counter->join();

var_dump($counter->i); // outputs a number from between 10 and 20

Tanpa menggunakan sinkronisasi, hasilnya tidak deterministik. Beberapa utas menulis ke satu variabel tanpa akses terkontrol telah menyebabkan pembaruan hilang

Mari perbaiki ini dengan menambahkan sinkronisasi sehingga kami menerima hasil yang benar dari

class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
6

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
0

Blok kode yang disinkronkan juga dapat bekerja sama satu sama lain menggunakan

class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
7 dan
class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
8 (bersama dengan
class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
9)

Inilah peningkatan yang mengejutkan dari dua while loop yang disinkronkan

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
_1

Anda mungkin telah memperhatikan kondisi tambahan yang ditempatkan di sekitar pemanggilan ke

class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
7. Ketentuan ini sangat penting karena hanya mengizinkan panggilan balik yang disinkronkan untuk dilanjutkan setelah menerima pemberitahuan dan ketentuan yang ditentukan adalah
$array = [1,2,3];

$task = new class($array) extends Thread {
    private $data;

    public function __construct(array $array)
    {
        $this->data = $array;
    }

    public function run()
    {
        $this->data[3] = 4;
        $this->data[] = 5;

        print_r($this->data);
    }
};

$task->start() && $task->join();

/* Output:
Volatile Object
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)
*/
1. Ini penting karena pemberitahuan mungkin berasal dari tempat selain panggilan ke
class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
8. Jadi, jika panggilan ke
class Task extends Volatile
{
    public function __construct()
    {
        $this->data = new Threaded();
        $this->data = new StdClass(); // valid, since we are in a volatile class
    }
}

$task = new class(new Task()) extends Thread {
    public function __construct($vm)
    {
        $this->volatileMember = $vm;

        var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}

        // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
        $this->volatileMember = new StdClass();
    }
};
_7 tidak disertakan dalam kondisi, kami akan terbuka untuk panggilan bangun palsu, yang akan menyebabkan kode yang tidak dapat diprediksi

Kesimpulan

Kita telah melihat lima paket pthreads kelas dengan itu (

class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
9,
class Task extends Threaded
{
    public $response;

    public function someWork()
    {
        $content = file_get_contents('http://google.com');
        preg_match('~<title>(.+)</title>~', $content, $matches);
        $this->response = $matches[1];
    }
}

$task = new Task;

$thread = new class($task) extends Thread {
    private $task;

    public function __construct(Threaded $task)
    {
        $this->task = $task;
    }

    public function run()
    {
        $this->task->someWork();
    }
};

$thread->start() && $thread->join();

var_dump($task->response);
2,
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
3,
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
4, dan
Threaded (implements Traversable, Collectable)
    Thread
        Worker
    Volatile
Pool
5), termasuk penutup ketika masing-masing kelas digunakan. Kami juga telah melihat konsep kekekalan baru di pthreads, serta melakukan tur singkat tentang fitur sinkronisasi yang didukungnya. Dengan dasar-dasar ini tercakup, kita sekarang dapat mulai melihat penerapan pthreads ke beberapa kasus penggunaan dunia nyata. Itu akan menjadi topik posting kami selanjutnya

Sementara itu, jika Anda memiliki beberapa ide aplikasi tentang pthreads, jangan ragu untuk menuliskannya di bawah ini ke area komentar

Bagikan Artikel Ini

Thomas Punt

Thomas adalah mahasiswa Teknologi Web yang baru saja lulus dari Inggris. Dia memiliki minat yang kuat dalam pemrograman, dengan fokus khusus pada teknologi pengembangan web sisi server (khususnya PHP dan Elixir). Dia berkontribusi pada PHP dan proyek sumber terbuka lainnya di waktu luangnya, serta menulis tentang topik yang menurutnya menarik

Apakah multithreading dimungkinkan dalam PHP?

Aplikasi PHP, tidak diragukan lagi bekerja secara efektif dengan kemampuan multithreading . Multithreading adalah sesuatu yang mirip dengan multitasking, tetapi memungkinkan untuk memproses banyak pekerjaan sekaligus, bukan pada banyak proses.

Bagaimana cara membuat multi utas PHP?

PHP tidak memberikan fungsionalitas multi-threading bawaan, kita perlu menambahkan "utas" paket/ekstensi ke PHP kita . Objek Berulir. Kelas yang merupakan unit instruksi yang dapat dieksekusi (utas), Ini adalah apa yang ingin Anda jalankan secara tidak sinkron. metode run di kelas ini memiliki kemampuan untuk mengeksekusinya sebagai utas.

Apakah PHP mendukung threading?

Aplikasi PHP dapat membuat, membaca, menulis, mengeksekusi, dan menyinkronkan dengan objek Threads, Worker, dan Threaded . Ekstensi ini dianggap tidak terawat dan mati. Pertimbangkan untuk menggunakan paralel sebagai gantinya. Ekstensi pthreads tidak dapat digunakan di lingkungan server web.

Bagaimana cara membuat utas di PHP?

Utasan PHP .
Objek Berulir. Kelas yang merupakan unit instruksi yang dapat dieksekusi (utas), Ini adalah apa yang ingin Anda jalankan secara tidak sinkron. .
Pekerja. Ini adalah kelas yang digunakan untuk menangani utas, membuat hasil disinkronkan
Kolam. Kelas ini digunakan untuk menangani beberapa instance kelas pekerja dan mengelolanya