Skip to content

Commit c33f363

Browse files
committed
Initial commit
0 parents  commit c33f363

File tree

5 files changed

+297
-0
lines changed

5 files changed

+297
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
examples

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 andrey-tech
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Parallel Executor
2+
3+
Простой класс на PHP 7.2+, позволяющий выполнять задачи в нескольких отдельных параллельных потоках PHP при помощи расширения [parallel](https://www.php.net/manual/ru/book.parallel.php).
4+
5+
## Содержание
6+
<!-- MarkdownTOC levels="1,2,3,4,5,6" autoanchor="true" autolink="true" -->
7+
8+
- [Требования](#%D0%A2%D1%80%D0%B5%D0%B1%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)
9+
- [Установка](#%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0)
10+
- [Класс `\App\ParallelExecutor`](#%D0%9A%D0%BB%D0%B0%D1%81%D1%81-appparallelexecutor)
11+
- [Методы класса](#%D0%9C%D0%B5%D1%82%D0%BE%D0%B4%D1%8B-%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B0)
12+
- [Дополнительные параметры](#%D0%94%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D1%8B)
13+
- [Примеры](#%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B)
14+
- [Автор](#%D0%90%D0%B2%D1%82%D0%BE%D1%80)
15+
- [Лицензия](#%D0%9B%D0%B8%D1%86%D0%B5%D0%BD%D0%B7%D0%B8%D1%8F)
16+
17+
<!-- /MarkdownTOC -->
18+
19+
<a id="%D0%A2%D1%80%D0%B5%D0%B1%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F"></a>
20+
## Требования
21+
22+
- PHP >=7.2 Thread Safe.
23+
- PHP-расширение [parallel](https://www.php.net/manual/ru/book.parallel.php).
24+
- Произвольный автозагрузчик классов, реализующий стандарт [PSR-4](https://www.php-fig.org/psr/psr-4/).
25+
26+
<a id="%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0"></a>
27+
## Установка
28+
29+
Установка через composer:
30+
```
31+
$ composer require andrey-tech/parallel-executor:"^1.0"
32+
```
33+
или добавить
34+
```
35+
"andrey-tech/parallel-executor": "^1.0"
36+
```
37+
в секцию require файла composer.json.
38+
39+
<a id="%D0%9A%D0%BB%D0%B0%D1%81%D1%81-appparallelexecutor"></a>
40+
## Класс `\App\ParallelExecutor`
41+
42+
<a id="%D0%9C%D0%B5%D1%82%D0%BE%D0%B4%D1%8B-%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B0"></a>
43+
### Методы класса
44+
45+
- `__construct(int $threads = 5, string $channelName = __CLASS__, int $channelСapacity = Channel::Infinite)`
46+
Конструктор класса.
47+
* `$threads` - количество создаваемых сред исполнения, как отдельных потоков PHP;
48+
* `$channelName` - имя создаваемого именованного канала;
49+
* `$channelСapacity` - емкость создаваемого именованного канала, МиБ (0 - небуферизированный канал)
50+
- `execute(\Closure $closure, array $argv = []) :void`
51+
Отправляет на исполнение переданную задачу.
52+
* `$closure` - функция-замыкание, исполняющяя задачу;
53+
* `$argv` - аргументы функции.
54+
55+
<a id="%D0%94%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D1%8B"></a>
56+
### Дополнительные параметры
57+
58+
Дополнительные параметры устанавливаются через публичные статические свойства класса `\App\ParallelExecutor`:
59+
60+
Свойство | По умолчанию | Описание
61+
----------------------- | ------------------ | --------
62+
`$autoloader` | '' | Устанавливает файл автозагрузчика классов, подключаемый в каждой среде исполнения
63+
64+
<a id="%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B"></a>
65+
## Примеры
66+
67+
Выполнение 10 задач в 3 параллельных потоках PHP:
68+
```php
69+
/*
70+
* Создаем исполнитель c 3-я отдельными параллельными потоками PHP
71+
* и буферизированным каналом с именем по умолчанию '\App\ParallelExecutor'
72+
*/
73+
$executor = new \App\ParallelExecutor(3);
74+
75+
$i = 0;
76+
$startTime = microtime(true);
77+
while ($i < 10) {
78+
$i++;
79+
$executor->execute(
80+
function ($i, $startTime) {
81+
$sleep = random_int(1, 5);
82+
printf("[%.4f] %2d: Start sleeping {$sleep} s..." . PHP_EOL, microtime(true) - $startTime, $i);
83+
sleep($sleep);
84+
printf("[%.4f] %2d: DONE" . PHP_EOL, microtime(true) - $startTime, $i);
85+
},
86+
[ $i, $startTime ]
87+
);
88+
}
89+
```
90+
91+
Результат:
92+
```
93+
[0.0002] 1: Start sleeping 4 s...
94+
[0.0002] 3: Start sleeping 1 s...
95+
[0.0002] 2: Start sleeping 1 s...
96+
[1.0015] 2: DONE
97+
[1.0015] 3: DONE
98+
[1.0019] 4: Start sleeping 1 s...
99+
[1.0061] 5: Start sleeping 3 s...
100+
[2.0069] 4: DONE
101+
[2.0073] 6: Start sleeping 5 s...
102+
[4.0011] 1: DONE
103+
[4.0014] 7: Start sleeping 4 s...
104+
[4.0073] 5: DONE
105+
[4.0077] 8: Start sleeping 2 s...
106+
[6.0084] 8: DONE
107+
[6.0088] 9: Start sleeping 2 s...
108+
[7.0118] 6: DONE
109+
[7.0121] 10: Start sleeping 1 s...
110+
[8.0066] 7: DONE
111+
[8.0136] 9: DONE
112+
[8.0166] 10: DONE
113+
```
114+
115+
116+
117+
118+
119+
<a id="%D0%90%D0%B2%D1%82%D0%BE%D1%80"></a>
120+
## Автор
121+
© 2020 andrey-tech
122+
123+
<a id="%D0%9B%D0%B8%D1%86%D0%B5%D0%BD%D0%B7%D0%B8%D1%8F"></a>
124+
## Лицензия
125+
Данный код распространяется на условиях лицензии [MIT](./LICENSE).

composer.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "andrey-tech/parallel-executor-php",
3+
"description": "Класс, выполняющий задачи в нескольких отдельных параллельных потоках PHP при помощи расширения parallel",
4+
"keywords": [ "parallel", "threads" ],
5+
"homepage": "https://github.com/andrey-tech/parallel-executor-php",
6+
"minimum-stability": "stable",
7+
"license": "MIT",
8+
"authors": [
9+
{
10+
"name": "andrey-tech",
11+
"homepage": "https://github.com/andrey-tech/",
12+
"role": "Developer"
13+
}
14+
],
15+
"require": {
16+
"php": ">=7.0"
17+
},
18+
"autoload": {
19+
"psr-4": {
20+
"App\\": "src/App"
21+
}
22+
}
23+
}

src/App/ParallelExecutor.php

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
/**
4+
* Класс ParallelExecutor.
5+
* Выполняет задачи в нескольких отдельных параллельных потоках PHP при помощи расширения parallel.
6+
*
7+
* @author andrey-tech
8+
* @copyright 2020 andrey-tech
9+
* @license MIT
10+
* @see https://github.com/andrey-tech/parallel-executor-php
11+
*
12+
* @version 1.0.0
13+
*
14+
* v1.0.0 (28.06.2020) Начальный релиз
15+
*
16+
*/
17+
18+
declare(strict_types = 1);
19+
20+
namespace App;
21+
22+
use parallel\{Runtime, Channel};
23+
24+
class ParallelExecutor
25+
{
26+
/**
27+
* Файл автозагрузчика классов, подключаемый в каждой среде исполнения
28+
* @var string
29+
*/
30+
public static $autoloader = '';
31+
32+
/**
33+
* Объект класса именованного канала \parallel\Channel
34+
* @var object
35+
*/
36+
private $channel;
37+
38+
/**
39+
* Массив объектов класса среды исполнения \parallel\Runtime
40+
* @var array
41+
*/
42+
private $workers = [];
43+
44+
/**
45+
* Конструктор
46+
* @param int $threads Количество создаваемых сред исполнения, как отдельных потоков PHP
47+
* @param string $channelName Имя создаваемого именованного канала
48+
* @param int $channelСapacity Емкость именованного канала, МиБ (0 - небуферизированный канал)
49+
*/
50+
public function __construct(
51+
int $threads = 5,
52+
string $channelName = __CLASS__,
53+
int $channelСapacity = Channel::Infinite
54+
) {
55+
// Создаем именованный небуферизированный или буферизированный канал
56+
if ($channelСapacity == 0) {
57+
$this->channel = Channel::make($channelName);
58+
} else {
59+
$this->channel = Channel::make($channelName, $channelСapacity);
60+
}
61+
62+
// Создаем заданное число сред исполнения, как отдельных потоков PHP
63+
for ($thread = 0; $thread < $threads; $thread++) {
64+
$this->workers[ $thread ] = new Runtime();
65+
$this->workers[ $thread ]->run(
66+
\Closure::fromCallable([ $this, 'thread' ]),
67+
[ $channelName, self::$autoloader ]
68+
);
69+
}
70+
}
71+
72+
/**
73+
* Отправляет на исполнение переданную задачу
74+
* @param \Closure $closure Функция-замыкание, исполняющая задачу
75+
* @param array $argv Аргументы функции
76+
* @return void
77+
*/
78+
public function execute(\Closure $closure, array $argv = []) :void
79+
{
80+
// Отправляем данные в именованный канал
81+
$this->channel->send([
82+
'closure' => $closure,
83+
'argv' => $argv
84+
]);
85+
}
86+
87+
/**
88+
* Исполняет задачи в отдельном потоке PHP
89+
* @param string $channelName Имя именованного канала
90+
* @param string $autoloader Файл автозагрузчика классов, подключаемый в каждой среде исполнения
91+
* @return void
92+
*/
93+
private function thread(string $channelName, string $autoloader) :void
94+
{
95+
// Подключаем файл автозагрузчика классов, если он задан
96+
if (! empty($autoloader)) {
97+
require_once $autoloader;
98+
}
99+
100+
// Открываем именованный канал
101+
$channel = Channel::open($channelName);
102+
103+
// Извлекаем данные из именованного канала и выполняем задачи
104+
while ($data = $channel->recv()) {
105+
($data['closure'])(...$data['argv']);
106+
}
107+
}
108+
109+
/**
110+
* Деструктор
111+
*/
112+
public function __destruct()
113+
{
114+
// Посылаем в канал признак завершения работы для каждой среды исполнения
115+
foreach ($this->workers as $worker) {
116+
$this->channel->send(false);
117+
}
118+
119+
// Отправляем всем средам исполнения сигнал завершения работы
120+
foreach ($this->workers as $worker) {
121+
$worker->close();
122+
}
123+
124+
// Закрываем именованный канал
125+
$this->channel->close();
126+
}
127+
}

0 commit comments

Comments
 (0)