This article describes how to install and use multiple threads in phppthreads. if you need to install Pthreads, you can refer to installing Pthreads and recompile PHP. the -- enable-maintainer-zts parameter is added, however, this document is rarely used. there will be many and many unexpected bugs in the bug generation environment, so you can only use it to play around. for real multithreading, you can still use Python, C, and so on.
I. Installation
Php-7.0.2 is used here
./configure \--prefix=/usr/local/php7 \--with-config-file-path=/etc \--with-config-file-scan-dir=/etc/php.d \--enable-debug \--enable-maintainer-zts \--enable-pcntl \--enable-fpm \--enable-opcache \--enable-embed=shared \--enable-json=shared \--enable-phpdbg \--with-curl=shared \--with-mysql=/usr/local/mysql \--with-mysqli=/usr/local/mysql/bin/mysql_config \--with-pdo-mysql
Make & make install
Install pthreads
Pecl install pthreads
II. Thread
<?php#1$thread = new class extends Thread {public function run() {echo "Hello World {$this->getThreadId()}\n"; } };$thread->start() && $thread->join();#2class workerThread extends Thread { public function __construct($i){$this->i=$i;}public function run(){while(true){echo $this->i."\n";sleep(1);} } }for($i=0;$i<50;$i++){$workers[$i]=new workerThread($i);$workers[$i]->start();}?>
III. Worker and Stackable
Stackables are tasks that are executed by Worker threads. You can be synchronize with, read, and write Stackable objects before, after and during their execution.
<?phpclass SQLQuery extends Stackable {public function __construct($sql) {$this->sql = $sql;}public function run() {$dbh = $this->worker->getConnection();$row = $dbh->query($this->sql);while($member = $row->fetch(PDO::FETCH_ASSOC)){print_r($member);}}}class ExampleWorker extends Worker {public static $dbh;public function __construct($name) {}public function run(){self::$dbh = new PDO('mysql:host=10.0.0.30;dbname=testdb','root','123456');}public function getConnection(){return self::$dbh;}}$worker = new ExampleWorker("My Worker Thread");$sql1 = new SQLQuery('select * from test order by id desc limit 1,5');$worker->stack($sql1);$sql2 = new SQLQuery('select * from test order by id desc limit 5,5');$worker->stack($sql2);$worker->start();$worker->shutdown();?>
IV. mutex lock
Under what circumstances will mutex lock be used? It can be used when you need to control multiple threads and only one thread can work at a time. A simple counter program is used to describe the differences in the case of mutex lock.
<? Php $ counter = 0; $ handle = fopen ("/tmp/counter.txt", "w"); fwrite ($ handle, $ counter); fclose ($ handle ); class CounterThread extends Thread {public function _ construct ($ mutex = null) {$ this-> mutex = $ mutex; $ this-> handle = fopen ("/tmp/counter.txt", "w +");} public function _ destruct () {fclose ($ this-> handle );} public function run () {if ($ this-> mutex) $ locked = Mutex: lock ($ this-> mutex); $ counter = intval (fgets ($ thi S-> handle); $ counter ++; rewind ($ this-> handle); fputs ($ this-> handle, $ counter ); printf ("Thread # % lu says: % s \ n", $ this-> getThreadId (), $ counter); if ($ this-> mutex) Mutex :: unlock ($ this-> mutex) ;}// no mutex lock for ($ I = 0; $ I <50; $ I ++) {$ threads [$ I] = new CounterThread (); $ threads [$ I]-> start ();} // add the mutex lock $ Mutex = mutex :: create (true); for ($ I = 0; $ I <50; $ I ++) {$ threads [$ I] = new CounterThread ($ mutex ); $ threads [$ I]-> start ();} Mute X: unlock ($ mutex); for ($ I = 0; $ I <50; $ I ++) {$ threads [$ I]-> join ();} mutex: destroy ($ mutex);?>
Multithreading and shared memory
In the example of shared memory, if no lock is used, it may still work normally, and the working memory operation itself may have the lock function.
<?php$tmp = tempnam(__FILE__, 'PHP');$key = ftok($tmp, 'a');$shmid = shm_attach($key);$counter = 0;shm_put_var( $shmid, 1, $counter );class CounterThread extends Thread {public function __construct($shmid){$this->shmid = $shmid;}public function run() {$counter = shm_get_var( $this->shmid, 1 );$counter++;shm_put_var( $this->shmid, 1, $counter );printf("Thread #%lu says: %s\n", $this->getThreadId(),$counter);}}for ($i=0;$i<100;$i++){$threads[] = new CounterThread($shmid);}for ($i=0;$i<100;$i++){$threads[$i]->start();}for ($i=0;$i<100;$i++){$threads[$i]->join();}shm_remove( $shmid );shm_detach( $shmid );?>
5. Thread synchronization
In some scenarios, we do not want thread-> start () to start the program, but want the thread to wait for our command. Thread −> wait (); the test function is that the thread does not run immediately after thread −> start (), and runs only after receiving the signal from thread-> notify ();
<?php$tmp = tempnam(__FILE__, 'PHP');$key = ftok($tmp, 'a');$shmid = shm_attach($key);$counter = 0;shm_put_var( $shmid, 1, $counter );class CounterThread extends Thread {public function __construct($shmid){$this->shmid = $shmid;}public function run() {$this->synchronized(function($thread){$thread->wait();}, $this);$counter = shm_get_var( $this->shmid, 1 );$counter++;shm_put_var( $this->shmid, 1, $counter );printf("Thread #%lu says: %s\n", $this->getThreadId(),$counter);}}for ($i=0;$i<100;$i++){$threads[] = new CounterThread($shmid);}for ($i=0;$i<100;$i++){$threads[$i]->start();}for ($i=0;$i<100;$i++){$threads[$i]->synchronized(function($thread){$thread->notify();}, $threads[$i]);}for ($i=0;$i<100;$i++){$threads[$i]->join();}shm_remove( $shmid );shm_detach( $shmid );?>
6. thread pool
One Pool class
<? Phpclass Update extends Thread {public $ running = false; public $ row = array (); public function _ construct ($ row) {$ this-> row = $ row; $ this-> SQL = null;} public function run () {if (strlen ($ this-> row ['bankno'])> 100) {$ bankno = safenet_decrypt ($ this-> row ['bankno']);} else {$ error = sprintf ("% s, % s \ r \ n ", $ this-> row ['id'], $ this-> row ['bankno']); file_put_contents ("bankno_error.log", $ error, FILE_APPEND);} if (Strlen ($ bankno)> 7) {$ SQL = sprintf ("update members set bankno = '% s' where id =' % s';", $ bankno, $ this-> row ['id']); $ this-> SQL = $ SQL;} printf ("% s \ n", $ this-> SQL );}} class Pool {public $ pool = array (); public function _ construct ($ count) {$ this-> count = $ count;} public function push ($ row) {if (count ($ this-> pool) <$ this-> count) {$ this-> pool [] = new Update ($ row); return true ;} else {return false ;}} public fun Ction start () {foreach ($ this-> pool as $ id = >$ worker) {$ this-> pool [$ id]-> start ();}} public function join () {foreach ($ this-> pool as $ id = >$ worker) {$ this-> pool [$ id]-> join ();}} public function clean () {foreach ($ this-> pool as $ id => $ worker) {if (! $ Worker-> isRunning () {unset ($ this-> pool [$ id]) ;}}try {$ dbh = new PDO ("mysql: host = ". str_replace (':', '; port =', $ dbhost ). "; dbname = $ dbname", $ dbuser, $ dbpw, array (PDO: MYSQL_ATTR_INIT_COMMAND => 'set NAMES \ 'utf8 \ '', PDO :: MYSQL_ATTR_COMPRESS => true); $ SQL = "select id, bankno from members order by id desc"; $ row = $ dbh-> query ($ SQL ); $ pool = new Pool (5); while ($ member = $ row-> fetch (PDO: FETCH_ASSOC )) {While (true) {if ($ pool-> push ($ member) {// Press the task into the pool break;} else {// if the pool is full, start the thread $ pool-> start (); $ pool-> join (); $ pool-> clean () ;}}$ pool-> start (); $ pool-> join (); $ dbh = null;} catch (Exception $ e) {echo '[', date ('H: I: s '), ']', 'system error', $ e-> getMessage (), "\ n" ;}?>
Dynamic queue thread pool
The above example is to execute start after the thread pool is full. the following example is to create a new thread immediately as long as the thread pool is idle.
<? Phpclass Update extends Thread {public $ running = false; public $ row = array (); public function _ construct ($ row) {$ this-> row = $ row; $ this-> SQL = null; // print_r ($ this-> row);} public function run () {if (strlen ($ this-> row ['bankno'])> 100) {$ bankno = safenet_decrypt ($ this-> row ['bankno']);} else {$ error = sprintf ("% s, % s \ r \ n", $ this-> row ['id'], $ this-> row ['bankno']); file_put_contents ("bankno_error.log", $ err Or, FILE_APPEND);} if (strlen ($ bankno)> 7) {$ SQL = sprintf ("update members set bankno = '% s' where id =' % s '; ", $ bankno, $ this-> row ['id']); $ this-> SQL = $ SQL;} printf (" % s \ n ", $ this-> SQL) ;}}try {$ dbh = new PDO ("mysql: host = ". str_replace (':', '; port =', $ dbhost ). "; dbname = $ dbname", $ dbuser, $ dbpw, array (PDO: MYSQL_ATTR_INIT_COMMAND => 'set NAMES \ 'utf8 \ '', PDO :: MYSQL_ATTR_COMPRESS => true); $ SQL = "sel Ect id, bankno from members order by id desc limit 50 "; $ row = $ dbh-> query ($ SQL); $ pool = array (); while ($ member = $ row-> fetch (PDO: FETCH_ASSOC) {$ id = $ member ['id']; while (true) {if (count ($ pool) <5) {$ pool [$ id] = new Update ($ member); $ pool [$ id]-> start (); break;} else {foreach ($ pool as $ name => $ worker) {if (! $ Worker-> isRunning () {unset ($ pool [$ name]) ;}}}$ dbh = null;} catch (Exception $ e) {echo '[', date ('H: I: s'), ']', '[system error]', $ e-> getMessage (), "\ n" ;}?>
Pthreads Pool class
<?phpclass WebWorker extends Worker {public function __construct(SafeLog $logger) {$this->logger = $logger;}protected $loger;}class WebWork extends Stackable {public function isComplete() {return $this->complete;}public function run() {$this->worker->logger->log("%s executing in Thread #%lu",__CLASS__, $this->worker->getThreadId());$this->complete = true;}protected $complete;}class SafeLog extends Stackable {protected function log($message, $args = []) {$args = func_get_args();if (($message = array_shift($args))) {echo vsprintf("{$message}\n", $args);}}}$pool = new Pool(8, \WebWorker::class, [new SafeLog()]);$pool->submit($w=new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->submit(new WebWork());$pool->shutdown();$pool->collect(function($work){return $work->isComplete();});var_dump($pool);
VII. secure read/write of multi-threaded files
LOCK_SH get Share Lock (read program)
LOCK_EX gets an exclusive lock (written program
LOCK_UN release lock (whether shared or exclusive)
LOCK_NB if you do not want flock () to be blocked during Lock
<? Php $ fp = fopen ("/tmp/lock.txt", "r +"); if (flock ($ fp, LOCK_EX )) {// lock ftruncate ($ fp, 0); // truncate filefwrite ($ fp, "Write something here \ n"); fflush ($ fp ); // flush output before releasing the lockflock ($ fp, LOCK_UN); // release lock} else {echo "Couldn't get the lock! ";}Fclose ($ fp); $ fp = fopen ('/tmp/lock.txt', 'R + '); if (! Flock ($ fp, LOCK_EX | LOCK_NB) {echo 'unable to obtain lock'; exit (-1) ;}fclose ($ fp);?>
8. multi-thread and data connection
Pthreads and pdo are used at the same time. Note that public static $ dbh must be declared statically and the database connection can be accessed in Singleton mode.
Worker and PDO
<?phpclass Work extends Stackable {public function __construct() {}public function run() {$dbh = $this->worker->getConnection();$sql = "select id,name from members order by id desc limit ";$row = $dbh->query($sql);while($member = $row->fetch(PDO::FETCH_ASSOC)){print_r($member);}}}class ExampleWorker extends Worker {public static $dbh;public function __construct($name) {}/** The run method should just prepare the environment for the work that is coming ...*/public function run(){self::$dbh = new PDO('mysql:host=...;dbname=example','www','');}public function getConnection(){return self::$dbh;}}$worker = new ExampleWorker("My Worker Thread");$work=new Work();$worker->stack($work);$worker->start();$worker->shutdown();?>
Pool and PDO
Connect to the database in the thread pool
# Cat pool. php <? Phpclass ExampleWorker extends Worker {public function _ construct (Logging $ logger) {$ this-> logger = $ logger;} protected $ logger ;} /* the collectable class implements machinery for Pool: collect */class Work extends Stackable {public function _ construct ($ number) {$ this-> number = $ number ;} public function run () {$ dbhost = 'DB .example.com '; // database server $ dbuser = 'example. com '; // database username $ dbpw = 'password '; // Database password $ dbname = 'example _ real'; $ dbh = new PDO ("mysql: host = $ dbhost; port =; dbname = $ dbname", $ dbuser, $ dbpw, array (PDO: MYSQL_ATTR_INIT_COMMAND => 'set NAMES \ 'utf \ ', PDO: MYSQL_ATTR_COMPRESS => true, PDO: ATTR_PERSISTENT => true )); $ SQL = "select OPEN_TIME, 'comment' from MT_TRADES where LOGIN = '". $ this-> number ['name']. "'and CMD ='' and 'comment' = '". $ this-> number ['order']. ": DEPOSIT '"; # echo $ SQL; $ ro W = $ dbh-> query ($ SQL); $ mt_trades = $ row-> fetch (PDO: FETCH_ASSOC); if ($ mt_trades) {$ row = null; $ SQL = "UPDATE db_example.accounts SET paystatus = 'success', deposit_time = '". $ mt_trades ['open _ time']. "'where'order' = '". $ this-> number ['order']. "';"; $ dbh-> query ($ SQL); # printf ("% s \ n", $ SQL) ;}$ dbh = null; printf ("runtime: % s, % s, % s \ n ", date ('Y-m-d H: I: s'), $ this-> worker-> getThreadId (), $ this-> number ['order']);} cl Ass Logging extends Stackable {protected static $ dbh; public function _ construct () {$ dbhost = 'DB .example.com '; // database server $ dbuser = 'example. com '; // database username $ dbpw = 'password'; // database password $ dbname = 'example _ real'; // database name self :: $ dbh = new PDO ("mysql: host = $ dbhost; port =; dbname = $ dbname", $ dbuser, $ dbpw, array (PDO :: MYSQL_ATTR_INIT_COMMAND => 'set NAMES \ 'utf \ '', PDO: MYSQL_ATTR_COMPRESS => true);} protected Function log ($ message, $ args = []) {$ args = func_get_args (); if ($ message = array_shift ($ args ))) {echo vsprintf ("{$ message} \ n", $ args) ;}} protected function getConnection () {return self ::$ dbh ;}} $ pool = new Pool (, \ ExampleWorker: class, [new Logging ()]); $ dbhost = 'DB .example.com '; // database server $ dbuser = 'example. com '; // database username $ dbpw = 'password'; // database password $ dbname = 'DB _ example'; $ dbh = new PDO ("mysql: host = $ Dbhost; port =; dbname = $ dbname ", $ dbuser, $ dbpw, array (PDO: MYSQL_ATTR_INIT_COMMAND => 'set NAMES \ 'utf \ ', PDO :: MYSQL_ATTR_COMPRESS => true); $ SQL = "select 'order', name from accounts where deposit_time is null order by id desc "; $ row = $ dbh-> query ($ SQL); while ($ account = $ row-> fetch (PDO: FETCH_ASSOC )) {$ pool-> submit (new Work ($ account) ;}$ pool-> shutdown ();?>
To further improve the above program, we use the Singleton mode $ this-> worker-> getInstance (); only one database connection is performed globally, and threads use a shared database connection.
<? Phpclass ExampleWorker extends Worker {# public function _ construct (Logging $ logger) {#$ this-> logger = $ logger ;#}# protected $ logger; protected static $ dbh; public function _ construct () {} public function run () {$ dbhost = 'DB .example.com '; // database server $ dbuser = 'example. com '; // database username $ dbpw = 'password'; // database password $ dbname = 'example'; // database name self :: $ dbh = new PDO ("mysql: host = $ dbhost; port =; dbname = $ dbname", $ Dbuser, $ dbpw, array (PDO: MYSQL_ATTR_INIT_COMMAND => 'set NAMES \ 'utf \ '', PDO: MYSQL_ATTR_COMPRESS => true, PDO :: ATTR_PERSISTENT => true);} protected function getInstance () {return self: $ dbh ;}/ * the collectable class implements machinery for Pool :: collect */class Work extends Stackable {public function _ construct ($ data) {$ this-> data = $ data; # print_r ($ data);} public function run () {# $ this-> worker-> l Ogger-> log ("% s executing in Thread # % lu", _ CLASS __, $ this-> worker-> getThreadId ()); try {$ dbh = $ this-> worker-> getInstance (); # print_r ($ dbh); $ id = $ this-> data ['id']; $ mobile = safenet_decrypt ($ this-> data ['mobile']); # printf ("% d, % s \ n", $ id, $ mobile ); if (strlen ($ mobile)>) {$ mobile = substr ($ mobile,-);} if ($ mobile = 'null ') {# $ SQL = "UPDATE members_digest SET mobile = '". $ mobile. "'Where id = '". $ id. "'"; # Printf ("% s \ n", $ SQL); # $ dbh-> query ($ SQL); $ mobile = ''; $ SQL = "UPDATE members_digest SET mobile =: mobile where id =: id";} else {$ SQL = "UPDATE members_digest SET mobile = md (: mobile) where id =: id ";}$…… =dbdbh-> prepare ($ SQL); $……-> bindValue (': mobile', $ mobile); $……-> bindValue (': ID', $ id); $ Something-> execute (); # echo $ Something-> debugDumpParams ();} catch (PDOException $ e) {$ error = sprintf ("% s, % s \ n", $ mobil E, $ id); file_put_contents ("mobile_error.log", $ error, FILE_APPEND) ;}# $ dbh = null; printf ("runtime: % s, % s \ n ", date ('Y-m-d H: I: s'), $ this-> worker-> getThreadId (), $ mobile, $ id ); # printf ("runtime: % s, % s \ n", date ('Y-m-d H: I: s'), $ this-> number );}} $ pool = new Pool (, \ ExampleWorker: class, []); # foreach (range (,) as $ number) {# $ pool-> submit (new Work ($ number); #}$ dbhost = 'DB .example.com '; // Database Server $ dbuser = 'example. com '; // database username $ dbpw = 'password'; // database password $ dbname = 'example'; $ dbh = new PDO ("mysql: host = $ dbhost; port =; dbname = $ dbname ", $ dbuser, $ dbpw, array (PDO: MYSQL_ATTR_INIT_COMMAND => 'set NAMES \ 'utf \ ', PDO :: MYSQL_ATTR_COMPRESS => true); # print_r ($ dbh); # $ SQL = "select id, mobile from members where id <: id "; # $ things = $ dbh-> prepare ($ SQL); # $ things-> bindValue (': ID',); # $ things-> execute (); # $ resul T = $ Something-> fetchAll (); # print_r ($ result); ##$ SQL = "UPDATE members_digest SET mobile =: mobile where id =: id "; # $ things = $ dbh-> prepare ($ SQL); # $ things-> bindValue (': mobile', 'A'); # $ things-> bindValue (': ID', ''); # echo $ Something-> execute (); # echo $ Something-> queryString; # echo $ Something-> debugDumpParams (); $ SQL = "select id, mobile from members order by id asc"; // limit "; $ row = $ dbh-> query ($ SQL ); while ($ members = $ row-> fetch (PDO :: FETCH_ASSOC) {# $ order = $ account ['order']; # printf ("% s \ n", $ order); // print_r ($ members ); $ pool-> submit (new Work ($ members); # unset ($ account ['order']);} $ pool-> shutdown ();?>
Summary of multi-thread database operations
In general, pthreads is still in development and there are still some shortcomings. we can also see that pthreads git is constantly improving this project.
The persistent connection of the database is very important. otherwise, each thread starts a database connection and closes the connection, which may cause many connections to time out.
<?php$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));?>
We will introduce you to the installation and use of php pthreads multithreading, which will be updated in the future.