pthreads 是一组允许用户在 PHP 中使用多线程技术的面向对象的 API。 它提供了创建多线程应用所需的全套工具。 通过使用 Thread, Worker 以及 Threaded 对象,PHP 应用可以创建、读取、写入以及执行多线程应用,并可以在多个线程之间进行同步控制。
不可以在 web 服务器环境中使用 pthreads 扩展, PHP 多线程开发仅限于命令行模式的应用。
只能在 PHP 7.2+ 版本中使用 pthreads (v3) 扩展, 在 PHP 7.0 和 7.1 版本中,ZTS 模式是不安全的。
Threaded 对象: Threaded 对象提供支持 pthreads 操作的基本功能,包括同步方法以及其他对程序员很有帮助的接口。
Thread 对象: 通过继承 pthreads 中提供的 Thread 对象并实现 run 方法,用户可以创建自己的 Thread 对象。 只要线程上下文中持有某个 Thread 对象的引用,就可以读/写该对象的属性,也可以调用该对象的公有(public)或者受保护(protected)的方法。 当在创建 Thread 对象的上下文中调用该对象的 Thread::start() 方法时,pthreads 扩展会在另外的独立线程中执行该对象的 run 方法。 仅有创建 Thread 对象的线程/进程方可开始(start)或者加入(join)这个 Thread 对象。
Worker 对象: Worker 是有状态的线程对象,它在线程开始(通过调用 Thread::start() 方法)之后就可用, 除非代码中显式地关闭线程(通过调用 Worker::shutdown() 方法), 否则该对象在线程对象超出作用范围之后才会失效。 持有 Worker 对象引用的线程上下文可以向 Worker 中入栈(通过调用 Worker::stack() 方法)其他线程对象,Worker 对象将在独立线程中执行入栈对象的代码。 Woker 对象的 run 方法会在它的栈中入栈对象之前执行,这样就可以进行一些必需的资源初始化工作。
Pool 对象: Pool 对象是 Worker 线程对象池,可以用来在多个 Worker 对象之间分发 Threaded 对象, 这是最易用且高效的多线程编程方式。
Volatile 类是在 pthreads v3 中新增加的, 用来表示可变的 Threaded 类中的 Threaded 属性(默认情况下是不可变的)。 它也可以被用来在 Threaded 上下文中存储数组。
线程间同步: 由 pthreads 扩展创建的所有对象拥有内置的线程间同步机制, 和 Java 语言很类似, 使用 Threaded::wait() 和 Threaded::notify() 方法来实现线程间同步。 调用某一个对象的 Threaded::wait() 方法 会导致当前线程上下文进入等待状态, 等待另外一个线程上下文调用同一个对象的 Threaded::notify() 方法。 为 PHP Threaded 对象提供了强有力的线程间同步控制机制。
应用中会用在多线程场景中的对象都应该从 Threaded 类继承。
数据存储: 一般来说,任何可以序列化的数据类型都可以作为 Threaded 对象的属性,它可以从持有该对象引用的任何线程上下文读/写。 并不是所有的数据都采用序列化方式存储,比如基本类型就是以其真实形态存储的。 对于不是 Threaded 派生的对象,例如复杂类型、数组以及对象等,都是序列化存储的,可以从持有 Threaded 对象引用的任何线程上下文中读取和写入, 区别就在于对于 Threaed 的派生对象,设置它的成员变量的过程是在独立线程上下文中执行的。 对于 Threaded 派生对象,在同一时间,不同的线程上下文都可以从该对象中读取到同样的数据。
静态成员: 当创建新的线程上下文(Thread 或 Worker 对象)的时候,静态成员会被拷贝到新的上下文中。出于安全考虑,资源类型以及包含内部状态的对象类型的静态成员会被置空。 实际上这个特性实现了类似线程本地存储的功能。举例说明,假设某个类拥有包含数据库连接信息以及数据库连接对象静态成员, 那么当新的线程上下文启动的时候,仅有数据库连接信息会被复制到新上下文中,而数据库连接对象并不会被复制。 所以,需要在新的上下文中根据复制过来的数据库连接基本信息来初始化数据库连接对象,新创建的数据库连接对象是独立的, 不影响在原上下文中的数据库连接对象。
当使用 print_r, var_dump 或者其他函数来进行对象调试的时候,是没有递归保护机制的。
Note:
资源类型: PHP 中很多使用到 Resource 资源类型的扩展或函数并未针对多线程场景进行特殊设计,也就是说,虽然 pthreads 扩展提供了 在多个线程上下文中共享资源类型变量的能力,但是通常来说,你应该把它们视为非线程安全的。 所以,如果要在多个线程上下文中共享资源类型的变量,你应该特别谨慎对待。
为了提供一个稳定的运行环境,pthreads 扩展在执行过程中会有一些必需的额外限制。