Amp PHP-协作多任务/异步睡眠

时间:2019-02-10 14:45:29

标签: php async-await

在单线程PHP中,我需要编写一个应用程序:

  • a)用作HTTP服务器
  • b)能够发出一些HTTP请求

a)和b)即使服务器正在等待/正在处理请求或HTTP客户端正在等待答复,也必须工作

我想到了使用PHP Amp服务器的想法。效果很好。

但是对于HTTP客户端,我需要使用PHP curl。

我的代码如下:

...
$mh = curl_multi_init();
curl_multi_add_handle($mh, $ch);
do {
    AmpLoopHelper::asyncSleep(0.001);
    $mrc = curl_multi_exec($mh, $isRunning);
} while ($isRunning && ($mrc == CURLM_CALL_MULTI_PERFORM || $mrc == CURLM_OK));
...

和自定义AmpLoopHelper类:

<?php

namespace Mvorisek\Dsv;

use Amp\Loop;
use Amp\Loop\Driver;


class AmpLoopHelper {
    /** @var int|null */
    private static $dummyWatcherId;

    /**
     * Async sleep and keep processing of Loop tasks.<br>
     * Loop\Driver::tick() is always called at least once even if
     * the sleep delay is zero or negative.
     * 
     * @param float $sleepSecs
     */
    public static function asyncSleep(float $sleepSecs): void {
        $t = microtime(true);

        // add dummy function to repeat to prevent Loop\Driver::tick() to block
        $maxCheckDelayMillis = min(max(1, $sleepSecs * 1000 / 50), 50);
        $isNested = static::$dummyWatcherId !== null;
        if (!$isNested) {
            static::$dummyWatcherId = Loop::repeat($maxCheckDelayMillis, static function() {});
        }

        try {
            do {
                if (static::loopDriverIsRunning()) {
                    static::loopDriverTick();
                } else {
                    usleep(($sleepSecs - (microtime(true) - $t)) * 1e6);
                    break;
                }
                usleep(40);
            } while(microtime(true) - $t < $sleepSecs);
        } finally {
            if (!$isNested) {
                Loop::cancel(static::$dummyWatcherId);
            }
        }
    }

    private static function loopDriverGet(): Driver {
        return \Closure::bind(static function() {
            return Loop::$driver;
        }, null, Loop::class)();
    }

    private static function loopDriverIsRunning(): bool {
        $driver = static::loopDriverGet();
        return \Closure::bind(static function() use($driver) {
            return $driver->running;
        }, null, Driver::class)();
    }

    private static function loopDriverTick(): void {
        $driver = static::loopDriverGet();
        \Closure::bind(static function() use($driver) {
            $driver->tick();
        }, null, Driver::class)();
    }
}

但是HTTP服务器有时无法访问。 helper类使用Amp类的一些私有方法。

asyncSleep的想法正确吗?

1 个答案:

答案 0 :(得分:0)

问题是Driver->dispatch()可能会被带阻塞标志调用,并且阻塞流没有从我的助手类中更新。