Async介紹

2018-10-06 10:30 更新

Hack提供了一種稱(chēng)為Async的功能,為您的程序提供了協(xié)作多任務(wù)的好處。它允許使用Async基礎(chǔ)架構(gòu)的代碼隱藏輸入/輸出(I / O)延遲和數(shù)據(jù)提取。因此,如果您的代碼具有涉及某種等待(例如,網(wǎng)絡(luò)訪問(wèn),等待數(shù)據(jù)庫(kù)查詢(xún))的操作,則async將您的程序停止運(yùn)行的停機(jī)時(shí)間最小化,因?yàn)槌绦驅(qū)?zhí)行其他操作可能會(huì)在其他地方進(jìn)一步的I / O。

Async 不是多線程 - HHVM仍然在一個(gè)主要請(qǐng)求線程中執(zhí)行所有的PHP / Hack代碼 - 但是其他操作(例如MySQL查詢(xún))現(xiàn)在可以執(zhí)行,而不需要在該代碼中使用的時(shí)間。

頁(yè)面作為依賴(lài)樹(shù)

想象一下,你有一個(gè)頁(yè)面包含兩個(gè)組件; 一個(gè)在MySQL中存儲(chǔ)數(shù)據(jù),另一個(gè)通過(guò)cURL從API獲取數(shù)據(jù),兩個(gè)緩存都導(dǎo)致Memcached。依賴(lài)關(guān)系可以這樣建模:

Async介紹

這樣的代碼結(jié)構(gòu)從Async中獲得最大的收益。

同步/阻塞IO:順序執(zhí)行

如果(像大多數(shù)PHP代碼)你不使用Async編程,每一步將一個(gè)接一個(gè)執(zhí)行:

Async介紹

Async執(zhí)行

所有PHP / Hack代碼在主請(qǐng)求線程中執(zhí)行,但是I / O不阻止它,并且多個(gè)I / O或其他Async任務(wù)可以同時(shí)執(zhí)行。如果您的代碼構(gòu)造為依賴(lài)樹(shù)并使用AsyncI / O,這將導(dǎo)致代碼的各個(gè)部分透明地交錯(cuò)而不是彼此阻塞:

Async介紹

重要的是,您的代碼執(zhí)行的順序是不能保證的 - 例如,如果組件A的cURL請(qǐng)求速度較慢,則執(zhí)行相同的代碼可能會(huì)更像這樣:

Async介紹

以這種方式重新排序不同的任務(wù)指令,可以隱藏I / O 延遲。因此,當(dāng)一個(gè)任務(wù)當(dāng)前處于I / O指令(例如,等待數(shù)據(jù))時(shí),另一個(gè)任務(wù)的指令,希望能夠減少延遲,可以在此期間執(zhí)行。

限制

兩個(gè)最重要的限制是:

  • 所有PHP / Hack代碼在主請(qǐng)求線程中執(zhí)行
  • 阻塞的API(例如mysql_query()sleep())不要讓她自動(dòng)轉(zhuǎn)換為Async功能-這將是不安全的,因?yàn)樗梢愿淖兊?,可能不能設(shè)計(jì)了這樣的可能性不相關(guān)的代碼的執(zhí)行順序。

例如,給出這個(gè)代碼:

<?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());

新用戶(hù)經(jīng)常認(rèn)為與多線程Async,所以期望do_cpu_work()并且do_sleep()并行執(zhí)行 - 但是,這不會(huì)發(fā)生,因?yàn)闆](méi)有可以移動(dòng)到后臺(tái)的操作:

  • do_cpu_work() 只包含沒(méi)有內(nèi)置的PHP代碼,所以執(zhí)行并阻止主請(qǐng)求線程
  • 雖然do_sleep()調(diào)用一個(gè)內(nèi)置的,它不是一個(gè)Async內(nèi)置的 - 所以它也必須阻止主請(qǐng)求線程

Async介紹

Async實(shí)踐:cURL

在沒(méi)有Async的情況下使兩個(gè)cURL請(qǐng)求的一種天真的方式可能如下所示:

<?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

在上面的示例中,對(duì)curl_exec()in 的調(diào)用curl_A()阻止了任何其他處理。因此,即使curl_B()是獨(dú)立的電話(huà)curl_A(),curl_A()在開(kāi)始執(zhí)行之前也必須等待完成:

Async介紹

幸運(yùn)的是,HHVM提供了一個(gè)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()允許調(diào)度程序在等待cURL的響應(yīng)時(shí)運(yùn)行其他代碼。最可能的行為是,當(dāng)我們還在等待調(diào)用時(shí)curl_B(),調(diào)度程序?qū)⑦x擇調(diào)用它,這反過(guò)來(lái)又會(huì)啟動(dòng)另一個(gè)Asynccurl_exec()。由于HTTP請(qǐng)求通常較慢,因此主線程將處于空閑狀態(tài),直到其中一個(gè)請(qǐng)求完成:

Async介紹

這個(gè)執(zhí)行令是最有可能的,但不是保證; 例如,如果curl_B()請(qǐng)求比curl_A()HTTP請(qǐng)求快得多,curl_B()可以在完成之前curl_A()完成。

Async加速此示例的數(shù)量可能會(huì)有很大差異(例如,根據(jù)網(wǎng)絡(luò)條件和DNS緩存),但可能很重要:

Async

Total time taken: 0.74790596961975 seconds

Non-Async

Total time taken: 1.050155878067 seconds
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)