Async介绍

2018-10-06 10:30 更新

Hack提供了一种称为Async的功能,为您的程序提供了协作多任务的好处。它允许使用Async基础架构的代码隐藏输入/输出(I / O)延迟和数据提取。因此,如果您的代码具有涉及某种等待(例如,网络访问,等待数据库查询)的操作,则async将您的程序停止运行的停机时间最小化,因为程序将执行其他操作可能会在其他地方进一步的I / O。

Async 不是多线程 - HHVM仍然在一个主要请求线程中执行所有的PHP / Hack代码 - 但是其他操作(例如MySQL查询)现在可以执行,而不需要在该代码中使用的时间。

页面作为依赖树

想象一下,你有一个页面包含两个组件; 一个在MySQL中存储数据,另一个通过cURL从API获取数据,两个缓存都导致Memcached。依赖关系可以这样建模:

Async介绍

这样的代码结构从Async中获得最大的收益。

同步/阻塞IO:顺序执行

如果(像大多数PHP代码)你不使用Async编程,每一步将一个接一个执行:

Async介绍

Async执行

所有PHP / Hack代码在主请求线程中执行,但是I / O不阻止它,并且多个I / O或其他Async任务可以同时执行。如果您的代码构造为依赖树并使用AsyncI / O,这将导致代码的各个部分透明地交错而不是彼此阻塞:

Async介绍

重要的是,您的代码执行的顺序是不能保证的 - 例如,如果组件A的cURL请求速度较慢,则执行相同的代码可能会更像这样:

Async介绍

以这种方式重新排序不同的任务指令,可以隐藏I / O 延迟。因此,当一个任务当前处于I / O指令(例如,等待数据)时,另一个任务的指令,希望能够减少延迟,可以在此期间执行。

限制

两个最重要的限制是:

  • 所有PHP / Hack代码在主请求线程中执行
  • 阻塞的API(例如mysql_query()sleep())不要让她自动转换为Async功能-这将是不安全的,因为它可以改变的,可能不能设计了这样的可能性不相关的代码的执行顺序。

例如,给出这个代码:

<?hh

namespace Hack\UserDocumentation\Async\Intro\Examples\Limtations;

async function do_cpu_work(): Awaitable<void> {
  print("Start CPU work\n");
  $a = 0;
  $b = 1;

  $list = [$a, $b];
 
  for ($i = 0; $i < 1000; ++$i) {
    $c = $a + $b;
    $list[] = $c;
    $a = $b;
    $b = $c;
  }
  print("End CPU work\n");
}

async function do_sleep(): Awaitable<void> {
  print("Start sleep\n");
  sleep(1);
  print("End sleep\n");
}

async function main(): Awaitable<void> {
  print("Start of main()\n");
  await \HH\Asio\v([
    do_cpu_work(),
    do_sleep(),
  ]);
  print("End of main()\n");
}

\HH\Asio\join(main());

新用户经常认为与多线程Async,所以期望do_cpu_work()并且do_sleep()并行执行 - 但是,这不会发生,因为没有可以移动到后台的操作:

  • do_cpu_work() 只包含没有内置的PHP代码,所以执行并阻止主请求线程
  • 虽然do_sleep()调用一个内置的,它不是一个Async内置的 - 所以它也必须阻止主请求线程

Async介绍

Async实践:cURL

在没有Async的情况下使两个cURL请求的一种天真的方式可能如下所示:

<?hh

namespace Hack\UserDocumentation\Async\Intro\Examples\NonAsyncCurl;

function curl_A(): mixed {
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, "http://example.com/");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  return curl_exec($ch);
}

function curl_B(): mixed {
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, "http://example.net/");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  return curl_exec($ch);
}

function main(): void {
  $start = microtime(true);
  $a = curl_A();
  $b = curl_B();
  $end = microtime(true);
  echo "Total time taken: " . strval($end - $start) . " seconds" . PHP_EOL;
}

main();

Output

Total time taken: 1.050155878067 seconds

在上面的示例中,对curl_exec()in 的调用curl_A()阻止了任何其他处理。因此,即使curl_B()是独立的电话curl_A()curl_A()在开始执行之前也必须等待完成:

Async介绍

幸运的是,HHVM提供了一个Async版本curl_exec()

<?hh

namespace Hack\UserDocumentation\Async\Intro\Examples\Curl;

async function curl_A(): Awaitable<string> {
  $x = await \HH\Asio\curl_exec("http://example.com/");
  return $x;
}

async function curl_B(): Awaitable<string> {
  $y = await \HH\Asio\curl_exec("http://example.net/");
  return $y;
}

async function async_curl(): Awaitable<void> {
  $start = microtime(true);
  list($a, $b) = await \HH\Asio\v(array(curl_A(), curl_B()));
  $end = microtime(true);
  echo "Total time taken: " . strval($end - $start) . " seconds" . PHP_EOL;
}

\HH\Asio\join(async_curl());

Output

Total time taken: 0.74790596961975 seconds 

Async版本curl_exec()允许调度程序在等待cURL的响应时运行其他代码。最可能的行为是,当我们还在等待调用时curl_B(),调度程序将选择调用它,这反过来又会启动另一个Asynccurl_exec()。由于HTTP请求通常较慢,因此主线程将处于空闲状态,直到其中一个请求完成:

Async介绍

这个执行令是最有可能的,但不是保证; 例如,如果curl_B()请求比curl_A()HTTP请求快得多,curl_B()可以在完成之前curl_A()完成。

Async加速此示例的数量可能会有很大差异(例如,根据网络条件和DNS缓存),但可能很重要:

Async

Total time taken: 0.74790596961975 seconds

Non-Async

Total time taken: 1.050155878067 seconds
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号