多线程编程·parallel\bootstrap()的使用

<?php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);


// parallel\bootstrap()可以看作是子线程的require()
parallel\bootstrap(__DIR__ . '/parallel.common.php');


// parallel\bootstrap()只能调用一次,否则会报如下错误:
// PHP Fatal error:  Uncaught parallel\Runtime\Error\Bootstrap: \parallel\bootstrap already set
// parallel\bootstrap(__DIR__ . '/Human1.class.php');
// parallel\bootstrap(__DIR__ . '/Human2.class.php');


// 启动子线程(1)
parallel\run(static function (string $name, string $gender, int $age): void {
    if (class_exists('Human1')) {
        echo 'Human1类存在' . PHP_EOL;
        $human1 = new Human1($name, $gender, $age);
        $human1->profile();
    } else {
        echo 'Human1类不存在' . PHP_EOL;
    }

    global $counter; // $counter是parallel.common.php声明的变量,想在闭包函数里使用需要使用global修饰
    $counter++;
    echo "子线程(1):\$counter = $counter" . PHP_EOL;
}, ['张三', '男', 18]);


// 休眠3秒钟再启动一个子线程
sleep(3);
echo '……(休眠3秒后)……' . PHP_EOL;


// 启动子线程(2)
parallel\run(static function (string $name, string $gender, int $age): void {
    if (class_exists('Human2')) {
        echo 'Human2类存在' . PHP_EOL;
        $human2 = new Human2($name, $gender, $age);
        $human2->profile();
    } else {
        echo 'Human2类不存在' . PHP_EOL;
    }

    global $counter; // $counter是parallel.common.php声明的变量,想在闭包函数里使用需要使用global修饰
    $counter++;
    echo "子线程(2):\$counter = $counter" . PHP_EOL;
}, ['李四', '女', 17]);


//========== 控制台输出·开始 ==========//

// Human1.class.php >> 若看到这句话则说明子线程已引入Human1类
// Human2.class.php >> 若看到这句话则说明子线程已引入Human2类
// parallel-bootstrap.php >> $counter的初始值为1024
// Human1类存在
// Human1::profile()  俺叫张三(男),今年18岁。
// 子线程(1):$counter = 1025
// ……(休眠3秒后)……
// Human2类存在
// Human2::profile()  俺叫李四(女),今年17岁。
// 子线程(2):$counter = 1026

//========== 控制台输出·结束 ==========//


//========== 总结 ==========//
// 1、子线程和主线程是代码隔离的,主线程里定义的类无法在子线程里使用,而parallel\bootstrap()可以看作是子线程的require_once(),通过该
//    方法可以在子线程里引入开发者自己的类库和函数库,从而在子线程里使用。
// 2、parallel\bootstrap()在主线程里只能调用一次,所以应该写一个common文件让其引入,然后在common文件里再引入开发者自己的类库和函数库。
// 3、parallel\bootstrap()引入的内容是所有子线程共享的,其中也包括普通变量,但是要注意的是这些变量需要使用global修饰才能在闭包函数里
//    使用。由于子线程是并发执行存在资源竞争问题,所以对于引入的变量应该只读不写,想在子线程之间传递数据必须使用管道或Redis等工具。
// 4、parallel\bootstrap()并不是调用时就立即执行参数指定的PHP脚本,而是有子线程启动才会执行,如果一直没有子线程启动那么就会一直等待。
//    并且由于子线程是并发执行,可能会出现两次执行脚本的问题,例如上面把休眠3秒注释掉就会出现两次执行parallel.common.php的情况,但这
//    并不会导致程序报错。



<?php
// parallel.common.php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);


// 引入开发者自己的类库
require_once __DIR__ . '/Human1.class.php';
require_once __DIR__ . '/Human2.class.php';


// 引入开发者自己的函数库
require_once __DIR__ . '/function.lib.php';


$counter = 1024;
echo "parallel-bootstrap.php >> \$counter的初始值为1024" . PHP_EOL;



<?php
// Human1.class.php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);


/**
 * Human1类
 */
class Human1
{
    private string $name;
    private string $gender;
    private int $age;

    public function __construct(string $name, string $gender, int $age)
    {
        $this->name = $name;
        $this->gender = $gender;
        $this->age = $age;
    }

    public function profile(): void
    {
        echo "Human1::profile()  俺叫{$this->name}{$this->gender}),今年{$this->age}岁。" . PHP_EOL;
    }
}


echo 'Human1.class.php >> 若看到这句话则说明子线程已引入Human1类' . PHP_EOL;



<?php
// Human2.class.php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);


/**
 * Human2类
 */
class Human2
{
    private string $name;
    private string $gender;
    private int $age;

    public function __construct(string $name, string $gender, int $age)
    {
        $this->name = $name;
        $this->gender = $gender;
        $this->age = $age;
    }

    public function profile(): void
    {
        echo "Human2::profile()  俺叫{$this->name}{$this->gender}),今年{$this->age}岁。" . PHP_EOL;
    }
}


echo 'Human2.class.php >> 若看到这句话则说明子线程已引入Human2类' . PHP_EOL;

Copyright © 2024 码农人生. All Rights Reserved