### threadtask **Многопоточная задача на PHP: преимущества и особенности** Многопоточные задачи на PHP имеют ряд преимуществ, таких как небольшой объём занимаемой памяти и стабильность. Они также могут быть гибко применены для параллельной обработки задач. * Репозиторий Docker-контейнера: [docker-threadtask](https://hub.docker.com/repository/docker/talent518/threadtask/general). ### Компиляция и запуск экземпляра кода 1. Для компиляции PHP в режиме безопасности потоков необходимо использовать следующие настройки: * Для PHP 8: `--enable-zts`. * При компиляции PHP необходимо отключить внешнюю библиотеку GD, то есть не добавлять опцию `--with-external-gd`, вместо этого можно использовать `--with-external-gd=no`. 2. Запустите тестовый скрипт с помощью команды: `./threadtask init.php`. 3. В конфигурации PHP opcache.protect_memory должен быть установлен в 0, иначе будет выдана ошибка сегментации. Это связано с тем, что этот параметр является небезопасным для потоков переключателем. 4. Перезагрузите сигнал SIGUSR1 или SIGUSR2 с помощью команд: `kill -SIGUSR1 pid` или `task_wait(SIGUSR1)` в файле `init.php`. ### Функции #### 1. Задачи 1. **create_task**: создаёт задачу. Принимает следующие параметры: * $taskname — название задачи, которое будет использоваться в целевом потоке для создания константы THREAD_TASK_NAME. * $filename — полный или относительный путь к файлу PHP. * $params — аргументы командной строки, аналогичные аргументам `arg1`, `arg2`, `arg3`... в команде `php filename arg1 arg2 arg3...`. * $logfile — файл журнала для вывода. * $logmode — режим открытия файла. * $res — ресурс для ожидания завершения задачи. 2. **task_is_run**: проверяет, завершена ли задача. Принимает один параметр: $res, который является ресурсом, переданным из функции create_task. 3. **task_join**: ожидает завершения задачи. Принимает один параметр: $res, который является ресурсом, переданным из функции create_task. 4. **task_kill**: отправляет сигнал $sig задаче. Принимает два параметра: $res (ресурс, переданный из функции create_task) и $sig (сигнал потока). 5. **task_wait**: отправляет сигналы всем потокам и ожидает их завершения. Принимает один параметр: сигнал процесса, например, SIGINT, SIGTERM, SIGUSR1, SIGUSR2. 6. **task_get_delay**: возвращает количество секунд задержки при завершении PHP или прерывании по исключению. 7. **task_set_delay**: устанавливает задержку в секундах перед повторной попыткой выполнения задачи при возникновении исключения во время выполнения. Значение по умолчанию равно 1. 8. **task_get_num**: получает количество потоков или задач. Если используется с параметром is_max, равным true, возвращается максимальное количество потоков, в противном случае — текущее количество потоков или задач. Функция task_get_threads является псевдонимом для task_get_num. 9. **task_set_threads**: устанавливает максимальное количество потоков. Значение по умолчанию — 256. 10. **task_get_debug**: определяет, включена ли отладочная информация. 11. **task_set_debug**: включает или выключает отладочную информацию. Значение по умолчанию — false. 12. **task_get_run**: определяет, выполняется ли программа. 13. **task_set_run**: устанавливает состояние выполнения программы. Значение по умолчанию — true. 14. **pthread_sigmask**: устанавливает маску сигналов для текущего потока. Принимает три параметра: * how — тип действия над маской сигналов: SIG_BLOCK, SIG_UNBLOCK или SIG_SETMASK. * newset — список сигналов. * oldset — выходной параметр, возвращающий предыдущую маску сигналов. 15. **pthread_yield**: освобождает текущий поток от оставшегося времени процессора. 16. **is_main_task**: определяет, является ли текущий поток основным. #### 2. Общие переменные 1. **share_var_init**: инициализирует общие переменные. Может быть вызвана только в основном потоке. Принимает необязательный параметр size для указания количества переменных. 2. **share_var_exists**: определяет наличие указанной общей переменной. 3. **share_var_get**: считывает общую переменную. 4. **share_var_get_and_del**: считывает и удаляет общую переменную. 5. **share_var_put**: записывает значение в общую переменную. Требуется хотя бы один аргумент, представляющий ключ для поиска многомерного массива. Последний аргумент может объединять существующий массив или заменять его. 6. **share_var_inc**: увеличивает общую переменную на заданное значение. Возвращает результат операции. 7. **share_var_set**: записывает значение в общую переменную. 8. **share_var_set_ex**: записывает значение с заданным сроком действия в общую переменную. Срок действия устанавливается с помощью параметра expire. Значение 0 означает бесконечный срок действия. 9. **share_var_del**: удаляет общую переменную. 10. **share_var_clean**: очищает общие переменные. 11. **share_var_clean_ex**: очищает просроченные общие переменные. Параметр expire должен быть больше 0. 12. **share_var_count**: подсчитывает количество элементов в общей переменной. Возвращаемое значение: больше 0 для массива, меньше 0 для строки, true для объекта, null для несуществующего элемента, false в других случаях. 13. **share_var_destory**: очищает общие переменные, может быть вызвана только в главном потоке. #### 3. Безопасные для потоков общие переменные 1. **ts_var_declare**: объявляет безопасную для потоков общую переменную. Принимает три аргумента: * varname — имя переменной, если пусто, использует значение var. * var — переменная, используемая, когда varname пусто. * is_fd — если true, можно использовать функцию ts_var_fd. 2. **ts_var_fd**: экспортирует дескриптор файла канала сокета (можно использовать функции расширения sockets). Принимает два аргумента: * var — общая переменная, возвращённая функцией ts_var_declare. * is_write — указывает, является ли канал каналом записи. 3. **ts_var_expire**: устанавливает срок действия общей переменной. Принимает два аргумента: * var — общая переменная, возвращённая функцией ts_var_declare. * expire — срок действия, 0 означает отсутствие срока действия. 4. **ts_var_exists**: проверяет наличие общей переменной. Принимает два аргумента: * var — общая переменная, возвращённая функцией ts_var_declare. * key — ключ, может быть строкой или целым числом. 5. **ts_var_set**: сохраняет данные в общую переменную. Принимает четыре аргумента: * var — общая переменная, возвращённая функцией ts_var_declare. * key — ключ, строка, целое число или пустое значение, пустое значение добавляет значение к концу. * val — значение. * expire — срок действия, 0 означает отсутствие срока действия. 6. **ts_var_push**: помещает значения в очередь. Принимает два аргумента: * var — общая переменная, возвращённая функцией ts_var_declare. * val ... — значения для помещения в очередь. 7. **ts_var_pop**: извлекает последнее значение из очереди. Принимает два аргумента: * var — общая переменная, возвращённая функцией ts_var_declare. * &key — ссылка на ключ извлечённого значения. 8. **ts_var_shift**: извлекает первое значение из очереди. ``` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) NOT NULL, `email` varchar(100) NOT NULL, `password` varchar(32) NOT NULL, `salt` varchar(8) NOT NULL, `registerTime` datetime NOT NULL, `loginTime` datetime DEFAULT NULL, `loginTimes` int(11) NOT NULL DEFAULT 0, PRIMARY KEY (`uid`), UNIQUE KEY `username` (`username`), UNIQUE KEY `email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `test`.`user` VALUES (1,'admin','admin@test.com','cb13131a512ff854c8bc0dc0ba04e4db','12345678','2019-10-14 22:13:55','2021-03-24 14:49:48',7), (2,'test','test@test.com','0ee08e4a9e574f4afa0abfb5ca4e47f8','87654321','2019-10-14 22:13:55','2021-03-24 08:37:56',1), (3,'test2','test2@test.com','66b5a5d70de6e691aa9e011eb40bf62c','853532e8','2019-10-16 20:29:18',NULL,0), (4,'test3','test3@test.com','093865fe1fc39dedc288275781c12bfe','d03db269','2019-10-16 20:30:10',NULL,0), (5,'test4','test4@test.con','94e5d07b62a291858b6cdc902c30f924','cf34c642','2021-03-24 06:40:52','2021-03-24 08:13:17',1), (6,'test5','test5@test.com','178a46704b93cd1a6468fe81fc66ae55','f66966f9','2021-03-24 08:17:16','2021-03-24 08:17:54',1); /* 所有用户的密码都是123456 */ ``` **Использование ab для проведения стресс-тестирования, результаты следующие:** * **Apache + PHP + MySQL + Redis**: 5000 запросов выполнено за 14,264 секунды, пропускная способность составила 350,53, использование памяти превысило 5 ГБ. Если Apache использует mpm_event_module и PHP ZTS, то использование памяти примерно одинаково. * **ThreadTask + PHP + MySQL**: 5000 запросов выполнено за 4,354 секунды, пропускная способность — 1148,33, использование памяти — 665,07 МБ. **Заключение**: использование ThreadTask привело к повышению производительности более чем в два раза, что является хорошим результатом.