There is a need to perform a specific process multiple threads. I learned about the extension for php - pthreads.
For example, a simple script outside Laravel works fine and I liked the results. I decided to move in Laravel, and faced with the problem. Of course I searched in google, found some questions on stackoverflow, where replied the author of extension. But me did not help his answers, so I ask you to help me.
Answered Question extension author.
There is a class App\Commands\QuestionsParserCommand. Inside I created an instance of the class App\My\Questions\QuestionsParser and call the method init(). Then the code of the method init():
// Create a pool
$pool = new Pool($this->threads, ParserWorkers::class);
// Create a thread class
$thread = new class extends Threaded
{
public function run()
{
// The class will receive data from a provider
// that will be shared between threads through ParserWorkers.
// It will work with the API and store the data in the database.
// The need to work with the threads,
// because the data for processing an incredible amount.
echo '+';
}
};
// Start a threads
for ($i = 0; $i < $this->threads; $i++) {
$pool->submit($thread);
}
$pool->shutdown();
Class ParserWorkers inherits from Worker and yet has an empty method run().
As a result, I run the script and get a message in the log of php:
[13-Oct-2016 11:27:35 Europe/Moscow] PHP Fatal error: Uncaught Exception: Serialization of 'Closure' is not allowed in [no active file]:0
Stack trace:
#0 {main}
thrown in [no active file] on line 0
Information: Laravel 5.2.43, php 7.0.8, Windows
Thank you all!
First you can get acquainted with the strategy of the author of the library: https://github.com/krakjoe/pthreads-autoloading-composer
The strategy used here ensures that each thread (Worker) gets a thread local copy of the framework, or whatever is being loaded, but does not break the abilities of objects that descend from pthreads.
Secondly, to start working with laravel features (like Service Providers, Facades, Helpers, etc), you need to initialize laravel.
I looked at how to initialize the application in the tests/CreatesApplication.php file. The code below shows how to do this for Laravel 5.7 with php 7.2.
Important: specify the correct path to autoload.php and app.php relative to the Autoloader.php file location.
namespace App\Console\Commands;
use Worker;
use Illuminate\Contracts\Console\Kernel;
class Autoloader extends Worker
{
public function run()
{
require __DIR__. '/../../../vendor/autoload.php';
$app = require __DIR__.'/../../../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
}
public function start($options = PTHREADS_INHERIT_ALL)
{
return parent::start(PTHREADS_INHERIT_INI);
}
}
namespace App\Console\Commands;
use Exception;
use Threaded;
class OperatorThread extends Threaded
{
/**
* @var OperatorThreaded
*/
private $operator;
private $error;
public function __construct(OperatorThreaded $operator)
{
$this->operator = $operator;
}
public function run()
{
try {
$this->operator->handle();
} catch (Exception $exception) {
$this->error = (string) $exception;
}
}
public function getError() {
return $this->error;
}
}
namespace App\Console\Commands;
class OperatorThreaded
{
private $i;
public function __construct($i)
{
$this->i = $i;
}
public function handle()
{
sleep(rand(1,3));
if (app()->isBooted()) {
echo $this->i . PHP_EOL;
}
}
}
Now you can use Laravel with pthreads:
namespace App\Console\Commands;
use Pool;
use Illuminate\Console\Command;
class Test extends Command
{
protected $description = 'test';
protected $signature = 'test';
public function handle()
{
$operators = [];
for ($i=0; $i<5; $i++) {
$operators[] = new OperatorThreaded($i);
}
$pool = new Pool(count($operators), Autoloader::class);
foreach ($operators as $operator) {
$thread = new OperatorThread($operator);
$pool->submit($thread);
}
while ($pool->collect());
$pool->shutdown();
}
}
$ php artisan test
1
2
4
0
3
foreach ($this->pool as $w) {
$w->start(PTHREADS_INHERIT_ALL ^ PTHREADS_INHERIT_CLASSES);
}