I am using PThreads for multi-threading in php. I have installed and run it successfully on my XAMPP server on windows.
I have 100K records in my database and I want to run 20 threads in parallel.Every thread will call 5k record from database and process them.Here is my code for this
require('mailscript.php');
class My extends Thread{
function __construct() {
$this->mailscript = new mailscript();
}
function run(){
$this->mailscript->runMailScript(5000);
}
}
for($i=0;$i<20;$i++){
$pool[] = new My();
}
foreach($pool as $worker){
$worker->start();
}
foreach($pool as $worker){
$worker->join();
}
When I run this code it only run for approx 600 records per thread maximum.Is there any limit issue for number of threads in PThreads. What is the issue please help me
hie, here is how I would handle pthreads with your example with a collection method to be used if nescessary. Hope this will help you.
/*pthreads batches */
$batches = array();
$nbpool = 20; // cpu 10 cores
/* job 1 */
$list = [/* data1 */];
$batches[] = array_chunk($list, 5000);
/* job 2 */
$list2 = [/* data2 */];
$batches[] = array_chunk($list2, 10000);
/*final collected results*/
$resultFinal = [];
/* loop across batches */
foreach ($batches as $key => $chunks) {
/* for intermediate collection */
$data[$key] = [];
/* how many workers */
$workCount = count($chunks);
/* set pool job up to max cpu capabilities */
$pool = new Pool($nbpool, Worker::class);
/* pool cycling submit */
foreach (range(1, $workCount) as $i) {
$chunck = $chunks[$i - 1];
$pool->submit(new processWork($chunck));
}
/* on collection cycling */
$collector = function (\Collectable $work) use (&$data) {
/* is worker complete ? */
$isGarbage = $work->isGarbage();
/* worker complete */
if ($isGarbage) {
$data[$key] = $work->result;
}
return $isGarbage;
};
do {
/* collection on pool stack */
$count = $pool->collect($collector);
$isComplete = count($data) === $workCount;
} while (!$isComplete);
/* push stack results */
array_push($resultFinal, $data);
/* close pool */
$pool->shutdown();
}
class processWork extends \Threaded implements \Collectable {
private $isGarbage;
private $process;
public $result;
public function __construct($process) {
$this->process = $process;
}
public function run() {
$workerDone = array();
foreach ($this->process as $k => $el) {
/* whatever stuff with $this->process */
}
$this->result = $workerDone;
$this->isGarbage = true; // yeah, it s done
}
public function isGarbage(): bool {
return $this->isGarbage;
}
}
Related
I am working on improving a process in php so I resorted into using multithreading using Worker to manage my threads. But the threads in the worker executes one after the other not concurrently.
This is my sample code
namespace App\Engine\Threads\ThreadClass;
class StatePaperAttendancePDFThread extends \Threaded
{
private $write_folder_name;
private $center_code;
private $paper_id;
private $title;
public function __construct($write_folder_name, $center_code, $paper_id, $title)
{
$this->write_folder_name = $write_folder_name;
$this->center_code = $center_code;
$this->paper_id = $paper_id;
$this->title = $title;
}
public function run(){
echo " Retrieving paper attendance ".$this->center_code." ".$this->paper_id." ".\Thread::getCurrentThreadId()." ".date("H:i:s:u").PHP_EOL;
$artisan = 'C:/xampp/htdocs/attendance/artisan';
$cmd = "php $artisan attendancereport:center $this->write_folder_name $this->center_code $this->paper_id $this->title";
$return = exec($cmd, $output, $return_var);
echo $return;
}
}
foreach ($centers as $i=>$center){
$center_code = $center->getCenterCode();
$thread = new StatePaperAttendancePDFThread($folder_name, $center_code, $paper_id, $title);
$worker->stack($thread);
}
$worker->start(PTHREADS_INHERIT_ALL ^ PTHREADS_INHERIT_CLASSES);
$worker->shutdown();
but when I monitor it from the CLI using the time been printed I can see that none of the threads starts together. They all starts with some seconds interval
Please what am I missing
I was able to solve my problem by creating a customized Thread Pool Executor as below. Please I am open to suggestions and Improvement
class ThreadPoolExecutor{
private $poolSize;
private $threadPool;
private $done = false;
private $workingThreads;
public function __construct($poolSize, array $threadPool)
{
$this->poolSize = $poolSize;
$this->threadPool = $threadPool;
}
public function execute()
{
$this->parametersOk();
try {
while (!empty($this->threadPool)) {
$this->extractThreads();
foreach ($this->workingThreads as $thread) {
$thread->start(PTHREADS_INHERIT_ALL ^ PTHREADS_INHERIT_CLASSES);
}
foreach ($this->workingThreads as $thread) {
$thread->join();
}
}
$this->done = true;
} catch (\Exception $ex) {
var_dump($ex->getMessage());
}
}
private function parametersOk()
{
if (!is_array($this->threadPool))
throw new \RuntimeException("threadPool expected to be an array of threads");
if (count($this->threadPool) <= 0)
throw new \RuntimeException("expected at least an element in threadPool");
foreach ($this->threadPool as $thread) {
if (!is_subclass_of($thread, \Thread::class, false)) {
throw new \RuntimeException(" an element of threadPool does not extend class \\Thread");
}
}
if ($this->poolSize > count($this->threadPool)) {
throw new \RuntimeException("The number of threads set to execute can not be greater than the threadPool");
}
return true;
}
private function extractThreads()
{
$this->workingThreads = [];
$this->workingThreads = array_slice($this->threadPool, 0, $this->poolSize);
for ($i = 0; $i < count($this->workingThreads); $i++) {
array_shift($this->threadPool);
}
}
public function isDone()
{
return $this->done;
}
}
I will appreciate any addition or correction to this.
I`m writing a Timer script,using a signal.Every 1 second send a SIGALRM to the process,then catch the signal and check whether the there is a task to do.
I use pcntl_alarm(1) to send the signal,but the process excute-and-quit,before receive the signal.
I think whether there is a method to set the prcess blocked waiting for the signal.I have used base_event,but its much heavy.
So anybody can help me.Thanks a lot.
That`s my script(Sorry for the chinesse ^_^):
<?php
/**
*定时器
*/
class Timer
{
//保存所有定时任务
public static $task = array();
//定时间隔
public static $time = 1;
/**
*开启服务
*#param $time int
*/
public static function run($time = null)
{
if($time)
{
self::$time = $time;
}
self::installHandler();
pcntl_alarm(1);
do{ sleep(2); }while(true);
}
/**
*注册信号处理函数
*/
public static function installHandler()
{
echo "installHandler\n";
pcntl_signal(SIGALRM, array('Timer','signalHandler'),false);
pcntl_alarm(1);
}
/**
*信号处理函数
*/
public static function signalHandler()
{
self::task();
}
/**
*执行回调
*/
public function task()
{
if(empty(self::$task))
{
return ;
}
foreach(self::$task as $time => $arr)
{
$current = time();
$func = $arr['func'];
$argv = $arr['argv'];
$interval = $arr['interval'];
$persist = $arr['persist'];
if($current == $time)
{
call_user_func_array($func, $argv);
}
if($persist)
{
self::$task[$current+$interval] = $arr;
}
unset(self::$task[$time]);
}
pcntl_alarm(self::$time);
}
/**
*添加任务
public static function add($interval, $func, $argv,$persist = false)
{
if(is_null($interval))
{
return;
}
$time = time()+$interval;
self::$task[$time] = array('func'=>$func, 'argv'=>$argv, 'interval'=>$interval, 'persist'=>$persist);
}
/**
*删除所有定时器任务
*/
public function dellAll()
{
self::$task = array();
}
}
Now, I know the answer even though so many times has passed away for I had other jobs to finish...sorry for the late.So my answer is very simple.
sleep
is right for blocking a process, then the process receives a signal,and what I have missed is never capture it...so use
pcntl_signal_dispatch
function in a loop is exactly the way.
I am running 4 threads running in same time. (Threads are running work() function in same time in this case)
global $i;
$i = 1;
function work($address) {
while($i < 1000) {
$i++;
----
if($i == something) some job...
----
}
}
For some reason this don't do the job.
Threads do sometimes same circle in while, so I have later some duplicate values. (probably they have some critical section)
Any idea how to fix this ?
The counter object must be thread safe, it must also employ synchronized methods.
Follows is an example of such code:
<?php
class Counter extends Threaded {
public function __construct($value = 0) {
$this->value = $value;
}
/** protected methods are synchronized in pthreads **/
protected function increment() { return ++$this->value; }
protected function decrement() { return --$this->value; }
protected $value;
}
class My extends Thread {
/** all threads share the same counter dependency */
public function __construct(Counter $counter) {
$this->counter = $counter;
}
/** work will execute from job 1 to 1000, and no more, across all threads **/
public function run() {
while (($job = $this->counter->increment()) <= 1000) {
printf("Thread %lu doing job %d\n",
Thread::getCurrentThreadId(), $job);
}
}
protected $counter;
}
$counter = new Counter();
$threads = [];
while (($tid = count($threads)) < 4) {
$threads[$tid] = new My($counter);
$threads[$tid]->start();
}
foreach ($threads as $thread)
$thread->join();
?>
work() seems superfluous, this logic should be in the ::run function most likely.
How to convert code from core PHP to Pthread code
My core PHP code :
require_once 'xyz.php';
$count="0";
foreach($sites as $site)
{
require_once 'allsite/'.$site.'.php';
$siteObj = new $site;
$data[$count] = $siteObj->getsiteData($query,$cat);
$count++;
}
How can i execute foreach loop code in Pthread manner ( Multi-threading )
After foreach loop execution complete normal PHP stuff done (Means No Multi-Threading after foreach Loop)
Note : I already installed and config pthread in PHP
Comments included.
<?php
//Require Core Component
require_once("xyz.php");
$Threads = new StackableArray();
//A Global Storage that will be shared between threads.
$Storage = new Storage();
//Store {count} in Global Storage
$Storage['count'] = 0;
/*
* For Each Site, spawn a new thread, process the site and store result in Global Storage.
*/
foreach($sites as $site)
{
$Thread = new SiteProcess($Storage, $site);
$Thread->start();
$Threads[$re] = $Thread;
}
//When the threads are complete, join them(destroy the threads)
foreach($Threads as $Thread)
{
$Thread->join();
}
//A thread class
class SiteProcess extends Threads
{
private $Storage;
private $site;
public function __construct(Storage $Storage, $site)
{
$this->Storage = $Storage;
$this->site = $site;
}
public function run()
{
require_once("allsite/{$this->site}.php");
$siteObj = new $this->site;
$this->Storage[$this->Storage['count']] = $siteObj->getsiteData($query, $cat);
$this->Storage['count'] = $this->Storage['count'] + 1;
}
}
class StackableArray extends Stackable { public function run() { } };
class Storage extends Stackable { public function run() { } };
Im trying to make a pthreads class that continuously updates a variable that I'll be using elsewhere in the script. The pthreads class should pull the contents of a URL, assign the value to the variable, sleep and repeat.
$pair = array();
class Pair extends Thread {
public function __construct($url){
$this->url = $url;
}
public function run() {
global $pair;
while (true){
$pair = json_decode("https://btc-e.com/api/2/".$this->url."/ticker", true);
sleep(5);
}
}
}
$BTC_USD = new Pair("btc_usd");
while (true){
print_r($pair);
sleep(5);
}
$pair needs to be continuously updated and printed to the screen
<?php
define ('SECOND', 1000000);
class Repeater extends Thread {
public $url;
public $data;
public function __construct($url) {
$this->url = $url;
}
public function run() {
do {
/* synchronize the object */
$this->synchronized(function(){
/* fetch data into object scope */
$this->data = file_get_contents($this->url);
/* wait or quit */
if (!$this->stop)
$this->wait();
});
} while (!$this->stop);
}
public function stop() {
/* synchronize the object */
$this->synchronized(function(){
/* set stop */
$this->stop = true;
/* notify the object (could/should be waiting) */
$this->notify();
});
}
public function getData() {
/* fetch data / wait for it to arrive */
return $this->synchronized(function(){
return $this->data;
});
}
}
/* to provide a limit for the example */
$iterations = 5;
/* the url etc is not important */
$repeat = new Repeater("http://www.google.com");
$repeat->start();
/* this will only return when there is data */
while (($data = $repeat->getData())) {
var_dump(strlen($data));
$repeat->synchronized(function($repeat){
/* notify allows the thread to make he next request */
$repeat->notify();
/* waiting here for three seconds */
$repeat->wait(3 * SECOND);
}, $repeat);
if (!--$iterations) {
/* because examples should be sensible */
$repeat->stop();
break;
}
}
?>
It does not seem like a good use of resources to have a request per thread, you probably don't really want to do that in the real world. sleep is not suitable for use in multi-threading, it does not leave threads in a receptive state.
globals have no effect in the context of threading, they work as they usually do with regard to scope, but it is not inclusive of the scope of a thread.