pthreads

pthreads

PHP에서 멀티스레딩에 필요한 도구를 모두 제공하는 객체 지향 API예요. Threads, Workers, Threaded 객체를 만들고 읽고 쓰고 실행하며 동기화할 수 있습니다.

출처: pthreads

본문

pthreads는 PHP에서 멀티스레딩에 필요한 모든 도구를 제공하는 객체 지향 API입니다. PHP 애플리케이션이 Threads, Workers, Threaded 객체를 생성하고 읽고 쓰고 실행하며 동기화할 수 있게 해줘요.

먼저 알아두어야 할 경고가 몇 가지 있습니다.

  • 이 확장은 유지보수가 중단된 것으로 간주되며 사실상 죽은(dead) 확장이에요.
  • 대안으로 parallel을 사용하는 것을 권장합니다.
  • pthreads 확장은 웹 서버 환경에서 사용할 수 없어요. 따라서 PHP에서의 스레딩은 CLI 기반 애플리케이션으로만 제한됩니다.
  • pthreads(v3)는 PHP 7.2+에서만 쓸 수 있어요. 7.0과 7.1에서는 ZTS 모드가 안전하지 않기 때문입니다.

Threaded 클래스는 pthreads가 동작할 수 있게 해주는 기능의 기반입니다. 동기화 메서드와 함께, 프로그래머에게 유용한 몇몇 인터페이스를 노출합니다.

Thread 클래스는 단순히 이를 상속하고 run 메서드를 구현하면 스레드를 만들 수 있게 해줍니다. 스레드에 대한 참조를 가진 어떤 컨텍스트에서도 멤버를 읽고 쓸 수 있고, 어떤 컨텍스트에서도 public·protected 메서드를 실행할 수 있어요. 스레드를 만든 컨텍스트에서 Thread::start() 메서드를 호출하면 run 메서드의 본문이 별도의 스레드에서 실행됩니다. 스레드를 시작하고 조인할 수 있는 것은 스레드를 만든 컨텍스트뿐이에요.

Worker 클래스는 영속적인 상태를 가지며, Thread::start()(상속된 메서드) 호출부터 객체가 스코프를 벗어나거나 Worker::shutdown()으로 명시적으로 종료될 때까지 사용할 수 있습니다. 워커 객체에 대한 참조를 가진 어떤 컨텍스트도 Worker::stack()을 통해 워커에 태스크를 쌓을 수 있고, 이 태스크들은 별도의 스레드에서 워커에 의해 실행됩니다. 워커 객체의 run 메서드는 워커 스택 위의 어떤 객체보다 먼저 실행되므로, 실행될 객체들이 필요로 하는 리소스를 초기화할 수 있어요.

Pool 클래스는 실행할 Threaded 객체들을 분배할 워커 그룹을 만드는 데 씁니다. PHP 애플리케이션에서 여러 스레드를 쓰는 가장 쉽고 효율적인 방법이에요.

  • Pool 클래스는 Threaded 클래스를 상속하지 않으므로, 풀 기반 객체는 일반 PHP 객체로 취급됩니다. 그래서 그 인스턴스를 서로 다른 컨텍스트에서 공유해서는 안 됩니다.

Volatile 클래스는 pthreads v3에 새로 추가됐어요. Threaded 클래스의 변경 가능한(mutable) 프로퍼티를 나타내는 데 씁니다(기본적으로 이제 불변이라서). 또한 Threaded 컨텍스트에서 PHP 배열을 저장하는 데도 사용됩니다.

스레딩에서 동기화는 중요한 능력입니다. pthreads가 만드는 모든 객체는 (자바 프로그래머에게 익숙한) Threaded::wait()Threaded::notify() 형태의 내장 동기화를 가집니다. 어떤 객체에 대해 Threaded::wait()를 호출하면 해당 컨텍스트는 다른 컨텍스트가 같은 객체에 Threaded::notify()를 호출할 때까지 기다립니다. 이 메커니즘으로 PHP에서 Threaded 객체 간의 강력한 동기화가 가능해집니다.

  • 멀티스레드 부분에서 사용할 의도가 있는 객체는 반드시 Threaded를 상속해야 합니다.

데이터 저장: 경험상 직렬화할 수 있는 데이터 타입이라면 무엇이든 Threaded 객체의 멤버로 쓸 수 있고, Threaded 객체에 대한 참조를 가진 어떤 컨텍스트에서도 읽고 쓸 수 있어요. 모든 타입의 데이터가 직렬로 저장되지는 않습니다. 기본 타입은 실제 형태 그대로 저장되고, 복합 타입·배열·Threaded가 아닌 객체는 직렬화되어 저장됩니다. Threaded 객체를 제외하면, Threaded 객체의 멤버를 설정하는 데 쓰인 참조는 Threaded 객체 안의 참조와 분리됩니다. 같은 데이터는 Threaded 객체에 대한 참조를 가진 어떤 컨텍스트에서든 언제든 직접 읽을 수 있어요.

정적 멤버: 새 컨텍스트(ThreadWorker)가 만들어질 때 정적 멤버는 일반적으로 복사되지만, 리소스와 내부 상태를 가진 객체는(안전을 위해) 널(null) 처리됩니다. 이를 통해 일종의 스레드 로컬 저장소(thread local storage)처럼 동작하게 할 수 있어요. 예를 들어 어떤 클래스의 정적 멤버가 데이터베이스 서버의 연결 정보와 연결 자체를 포함한다면, 컨텍스트가 시작될 때는 단순한 연결 정보만 복사되고 연결은 복사되지 않습니다. 그래서 새 컨텍스트가 자신을 만든 컨텍스트와 같은 방식으로 연결을 시작하고, 원래 컨텍스트에 영향을 주지 않으면서 같은 자리에 연결을 저장할 수 있어요.

  • print_r, var_dump 같은 객체 디버그 함수는 재귀 보호를 포함하지 않습니다.

리소스: PHP에서 리소스를 정의하는 확장과 기능들은 이런 환경에 전혀 대비되어 있지 않습니다. pthreads는 리소스를 컨텍스트 간에 공유할 수 있도록 조치를 마련하지만, 대부분의 리소스 타입에 대해서는 안전하지 않다고 봐야 해요. 컨텍스트 간에 리소스를 공유할 때는 극도의 주의가 필요합니다.

pthreads가 실행되는 환경에서는 안정적인 환경을 제공하기 위해 몇 가지 제한과 한계가 필요합니다.

더 알아보기