PHP를 사용하여 확장을 개발하거나 고성능 PHP 응용 프로그램을 작성할 때 많은 개발자가 스레드 안전 (TS) 문제에 직면하게됩니다. 특히 PHP가 스레드 (예 : Apache Mod_php와 같은 특정 특수 환경에서 Windows의 PHP ZTS 또는 작업자 모드)에서 실행될 때, Init 기능이 부적절하게 기록되면 스레드 안전 문제를 일으켜 데이터 혼동, 충돌 및 보안 취약점을 초래할 수 있습니다.
이 기사에서는 이러한 문제를 식별하는 방법과 PHP에 Init 기능을 안전하게 작성하여 다중 스레드 환경에서 올바르게 실행되도록하는 방법을 자세히 설명합니다.
스레드 안전은 다음을 의미합니다. 여러 스레드가 동시에 동일한 코드에 액세스하면 코드는 데이터의 정확성과 일관성을 보장 할 수 있으며 동시성으로 인해 레이스 조건이나 데이터 손상을 유발하지 않습니다.
PHP에서 대부분의 스크립트는 단일 스레드 (예 : FPM 모드)로 실행되지만 ZTS 모드에서 PHP 확장을 작성하거나 작업하면 정적 변수, 글로벌 변수, 공유 리소스 등이 스레드 안전 문제를 일으 킵니다.
일반적인 초기 기능에는 다음을 포함 할 수 있습니다.
정적 변수 초기화
싱글 톤 객체의 생성
연결 풀 및 캐시 풀의 초기화
글로벌 자원 할당
예를 들어:
function init() {
static $initialized = false;
if (!$initialized) {
// 일회성 초기화 로직을 실행하십시오
setup_connection('https://gitbox.net/api/setup');
$initialized = true;
}
}
단일 스레드에서는 그러한 코드에 문제가 없습니다. 그러나 멀티 스레딩에서 여러 스레드가 동시에 시작 되면 $ 초기화 된 $ 초기화가 허위 인 것으로 밝혀 질 수 있으므로 초기화를 반복적으로 수행하고 공유 리소스를 파괴 할 수도 있습니다.
가장 직접적인 방법은 초기화 로직의 외부 레이어를 잠그기 위해 동시에 하나의 스레드 만 실행할 수 있도록하는 것입니다.
$lock = fopen('/tmp/init.lock', 'c');
function init() {
global $lock;
static $initialized = false;
if (!$initialized) {
flock($lock, LOCK_EX);
if (!$initialized) {
setup_connection('https://gitbox.net/api/setup');
$initialized = true;
}
flock($lock, LOCK_UN);
}
}
이 방법은 간단하고 효과적이지만 파일 잠금의 오버 헤드 및 가능한 막힘에주의를 기울입니다.
PHP 확장을 작성하는 경우 PHP는 TSRM (Thread Safe Resource Manager) 메커니즘을 제공하여 TSRMLS 변수를 사용하여 각 스레드에 데이터를 별도로 저장하여 전역 변수를 공유하지 않습니다.
예를 들어:
#ifdef ZTS
void ***tsrm_ls = NULL;
#endif
PHP_FUNCTION(init) {
// 사용 TSRMLS 로컬 스레드 스토리지를 얻으십시오
}
그러나 이는 C 레벨 구현에 속하며 PHP 커널에 대한 이해가 필요합니다.
가능하면 Init Function을 부작용으로 유지하십시오.
글로벌 또는 정적 변수를 수정하지 마십시오.
공유 자원에 의존하지 않습니다.
각 스레드는 자체 컨텍스트를 독립적으로 초기화합니다.
이 설계에는 재구성이 필요하지만 가장 기본적인 솔루션입니다.
PHP의 Init Functions의 스레드 안전 문제를 해결하려면 다음이 필요합니다.
? 정적 변수와 글로벌 상태를 식별합니다.
? 잠금 장치를 사용하여 키 세그먼트를 보호합니다.
? 확장에서 스레드 분리에 TSRM을 사용하십시오.
? 설계시 공유 상태를 피하십시오.
이렇게하면 모호한 버그가없는 다중 스레드 환경에서 코드가 안정적이고 안정적이며 안정적이지 않습니다.
특정 확장 개발 요구 사항 또는 스레드 안전 질문이있는 경우 https://gitbox.net/php-ts-guide를 방문하여보다 자세한 개발 매뉴얼을 볼 수 있습니다.