Pthreads扩展实现PHP多线程学习笔记

一、关于pthreads扩展

PHP本身不支持多线程,如果想再CLI模式下实现多线程,需要通过扩展pthreads。pthreads 是一组允许用户在PHP中使用多线程技术的面向对象的API。基于Posix Threads。

注:
POSIX(Portable Operating System Interface)是IEEE为要在各种UNIX操作系统上运行软件,而定义API的一系列互相关联的标准的总称, 也叫可移植操作系统接口

POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。Windows操作系统也有其移植版pthreads-win32。

0、PHP手册说明pthreads
1、github地址
https://github.com/krakjoe/pthreadsgithub中有很多example可以参考。

二、编译安装Pthreads

0、下载扩展

git clone https://github.com/krakjoe/pthreads.git

1、需要确认PHP是指定线程安全的方式编译的

/usr/local/php/bin/php --info | grep Thread
Thread Safety => enabled

如果不是线程安全的需要带上参数–enable-maintainer-zts,重新编译。

2、编译按照pthreads扩展

cd pthreads/

/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install

4、php.ini中添加扩展
把pthreads.so添加到php.ini文件中

/usr/local/php/bin/php -m | grep pthread
pthreads

5、测试
以下代码保存文件pthreads.php

$thread = new class extends Thread {
	public function run() {
		echo "Hello World\n";
	}
};

$thread->start() && $thread->join();

执行以上代码

/usr/local/php/bin/php pthreads.php
Hello World

三、多线程原理

0、TSRM机制
多线程模式下,多个线程共用一个进程的地址空间,即多个线程共享一个全局变量,这个时候就会产生竞争,也就是常说的这个全局变量是非线程安全的。PHP引入了一个TSRM(Thread-Safe Resource Manager)机制来解决线程安全问题。

这个机制的主要思想是:对于多线程模型来说,每当一个新的线程被创建,就单独的分配一块内存,这块内存存储着一个全局变量的副本。而这块内存会被一个Vector串起来,由Zend统一管理。(参加风雪之隅:揭秘TSRM(Introspecting TSRM))

1、线程类
在pthread扩展中,提供了创建多线程应用需要的全套工具。主要包含以下几个类,Threaded、Thread、Worker、Pool、Mutex等。

1.0、Threaded
提供多线程操作的基本功能。主要的是run方法, 每个线程都要实现此方法,线程开始运行后,此方法中的代码会自动执行;

1.1、Thread
继承了Threaded类,实现了run方法。Thread类中常用方法
Thread::start() 开始运行一个新线程,并且执行继承的run方法。
Thread::getThreadId() 获取当前线程ID。
Thread::join() 等待线程结束。
Thread::kill() 强制线程结束。

注:创建自己的线程类,通常直接继承Thread类。

1.2、Mutex(pthread 小于2.0版本,2.0以上使用Threaded::synchronized)
互斥锁相关功能。常用方法
Mutex::create 创建一个互斥锁
Mutex::lock 给互斥量加锁
Mutex::unlock 释放互斥量上的锁
Mutex::destroy 释放互斥锁

四、多线程实践
一、一个进程中创建多个线程执行打印操作
0、代码(以下保存文件multi_thread.php)

class workerThread extends Thread {
    private $i = null;

    public function __construct($i) {
        $this->i = $i;
    }
    
    public function run() {
        printf("%s is Thread #%lu, Pid=%s\n", __CLASS__, $this->getThreadId(), getmypid());
        while(true) {
            echo $this->i, PHP_EOL;
            sleep(10);
        }
    }
}
    
for($i = 0; $i < 10; $i++) {
    $workers[$i] = new workerThread($i);
    $workers[$i]->start();
}

1、执行结果

[root@izj6cfhaw27k49x8usszs3z thread]# php7 multi_thread.php
workerThread is Thread #140040849848064, Pid=6554
0
workerThread is Thread #140040764126976, Pid=6554
1
workerThread is Thread #140040755734272, Pid=6554
2
workerThread is Thread #140040747341568, Pid=6554
3
workerThread is Thread #140040738948864, Pid=6554
4
workerThread is Thread #140040726378240, Pid=6554
5
workerThread is Thread #140040713795328, Pid=6554
6
workerThread is Thread #140040361473792, Pid=6554
7
workerThread is Thread #140040353081088, Pid=6554
8
workerThread is Thread #140040344688384, Pid=6554
9

并行的打印出了上面的结果,等9后推出。

2、思考
把run方法中while去掉,也是同样的结果,为啥没有进入while轮询呢

二、多线程实现计数器程序
0、代码(以下代码保存在文件pthread_multi.php)

$counter = 0;
$handle = fopen("/tmp/counter.txt", "w");
fwrite($handle, $counter );
fclose($handle);

class CounterThread extends Thread {

	public function __construct($mutex = null) {
		$this->mutex = $mutex;
        $this->handle = fopen("/tmp/counter.txt", "w+");
    }

    public function run() {
		if($this->mutex)
			$locked = Mutex::lock($this->mutex);

		$counter = intval(fgets($this->handle));
		$counter++;
		rewind($this->handle);
		fputs($this->handle, $counter);
		printf("Thread #%lu says: %s\n", $this->getThreadId(), $counter);

		if($this->mutex)
			Mutex::unlock($this->mutex);
    }

    public function __destruct(){
		fclose($this->handle);
    }
}

//没有互斥锁
for ($i = 0; $i < 10; $i++) {
	$threads[$i] = new CounterThread();
	$threads[$i]->start();
}

// 加入互斥锁
$mutex = Mutex::create(true);
for ($i = 0; $i < 50; $i++) {
	$threads[$i] = new CounterThread($mutex);
	$threads[$i]->start();

}

Mutex::unlock($mutex);
for ($i = 0; $i < 50; $i++) {
	$threads[$i]->join();
}
Mutex::destroy($mutex);

1、执行
使用到了Mutex类,需要在pthread2.0以下版本使用。

php pthread_multi.php

查看pthread版本

/usr/local/php/bin/php --info | grep -C 10 -i 'pthread'

pthreads

Version => 3.2.1dev

执行报错”zend_mm_heap corrupted”(更多solutin点击stackoverflow What does zend_mm_heap corrupted mean)
在命令行执行如下命令

export USE_ZEND_ALLOC=0

2、使用synchronized实现计数器
pthreads v3下的同步处理synchronized

参考:
https://github.com/krakjoe/pthreads
Wikipedia:Thread (computing)
掘金:多线程编程 – PHP实现
PHP 高级编程之多线程
深入研究PHP及Zend Engine的线程安全模型

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注