Maison  >  Article  >  développement back-end  >  Compréhension approfondie des coroutines et du blocage en php

Compréhension approfondie des coroutines et du blocage en php

黄舟
黄舟original
2017-08-11 11:37:001673parcourir

Cet article vous présente principalement une certaine compréhension et réflexion sur les coroutines et le blocage en PHP. L'article le présente en détail à travers un exemple de code. Il a une certaine valeur d'apprentissage de référence pour que tous les amis qui en ont besoin apprennent. avec l'éditeur ci-dessous.

Avant-propos

Cet article vous présente principalement la compréhension et la réflexion sur les coroutines et le blocage en PHP, et le partage pour votre référence et votre étude . Pas grand chose à dire ci-dessous, jetons un œil à l'introduction détaillée :

Processus, thread, coroutine

À propos du processus. , les fils de discussion et les coroutines, il existe des blogs ou des ressources d'apprentissage très détaillés et riches. Je n'entrerai pas dans les détails ici, je présenterai brièvement ces choses ici.

  • Le processus a son propre tas et sa pile indépendants. Il ne partage ni le tas ni la pile. Le processus est planifié par le système d'exploitation.

  • Les threads ont leur propre pile indépendante et leur tas partagé. Le tas est partagé mais la pile n'est pas partagée. Les threads sont également planifiés par le système d'exploitation (les threads standard le sont).

  • Les coroutines partagent le tas comme les threads, mais ne partagent pas les piles. Les coroutines sont planifiées explicitement par les programmeurs dans le code de la coroutine.

Implémentation de base du rendement de la coroutine en PHP

L'implémentation fondamentale du rendement est de générer la classe Iterator, et la classe iterator est l'implémentation de l'interface iterator :


Generator implements Iterator {
 public mixed current ( void ) // 返回当前产生的值
 public mixed key ( void ) // 返回当前产生的键
 public void next ( void ) // 生成器继续执行
 public void rewind ( void ) // 重置迭代器,如果迭代已经开始了,这里会抛出一个异常。
   // renwind的执行将会导致第一个yield被执行, 并且忽略了他的返回值.
 public mixed send ( mixed $value ) // 向生成器中传入一个值,并且当做 yield 表达式的结果,然后继续执行生成器。如果当这个方法被调用时,生成器 
   // 不在 yield 表达式,那么在传入值之前,它会先运行到第一个 yield 表达式。
 public void throw ( Exception $exception ) // 向生成器中抛入一个异常
 public bool valid ( void ) // 检查迭代器是否被关闭
 public void __wakeup ( void ) // 序列化回调,抛出一个异常以表示生成器不能被序列化。
}

Je le ferai en fonction de la planification multitâche de la coroutine qu'il mis en œuvre, je vais vous donner un exemple et parler de certaines de mes réflexions sur le blocage.

Exemple de personnalisation de tâches d'exécution planifiées simples :

(Cet exemple doit s'appuyer sur le code de planification de coroutine implémenté par Brother Niao ci-dessus )


class timer {
 private $start = 0; // 定时开始时间
 private $timer; // 间隔的时间差,单位秒
 private $value = 0; // 产生的结果值
 private $callback; // 异步回调
 private $isEnd = false; // 当前定时器任务是否结束
 public function __construct($timer,callable $callback)
 {
 $this->start = time();
 $this->timer = $timer;
 $this->callback = $callback;
 }
 public function run() {
 if($this->valid()) {
 $callback = $this->callback;
 $callback($this->value ++,$this);
 $this->start = time();
 }
 }
 /**
 * 定时执行检查
 */
 public function valid() {
 $end = time();
 if($end - $this->start >= $this->timer) {
 return true;
 } else {
 return false;
 }
 }
 public function setEnd($isEnd) {
 $this->isEnd = $isEnd;
 }
 public function getEnd() {
 return $this->isEnd;
 }
}

/**
 * 模拟阻塞的协程1
 *
 */
function taskObject1() {
 $timer = new timer(1,function($value,timer $timer) {
 if($value >= 5) {
 $timer->setEnd(true);
 }
 echo &#39;<br>&#39;.&#39;A &#39;.$value;
 });
 $tid = (yield getTaskId());
 while (true) {
 if($timer->getEnd() == true) {
 break;
 }
 yield $timer->run();
 }
}
/**
 * 模拟阻塞的协程2
 *
 */
function taskObject2() {
 $timer = new timer(2,function($value,timer $timer) {
 if($value >= 3) {
 $timer->setEnd(true);
 }
 echo &#39;<br>&#39;.&#39;B &#39;.$value;
 });
 $tid = (yield getTaskId());
 while (true) {
 if($timer->getEnd() == true) {
 break;
 }
 yield $timer->run();
 }
}
$scheduler = new Scheduler;
$scheduler->newTask(taskObject1());
$scheduler->newTask(taskObject2());
$scheduler->run();

L'implémentation ci-dessus est :

  • Générer deux tâches, les exécuter en parallèle et attribuer à chaque tâche un Simulez quelques secondes de blocage pendant l'exécution ;

  • permet une commutation en douceur lorsque les coroutines sont commutées, et le blocage des tâches ne s'affecte pas

Réflexion :

Pourquoi devrais-je faire la chose ci-dessus ? Parce que j'ai trouvé que même si l'implémentation de la coroutine est très puissante et intéressante, et peut permettre le multitâche en parallèle, lorsque j'appelle la fonction système

dans l'une des tâches, la tâche de blocage empêchera en fait le changement de coroutine. le principe de mise en œuvre de la coroutine Il en va de même pour les livres. sleep()

Ensuite, je veux aussi simuler le blocage de la coroutine, mais ne pas provoquer de blocage pour voir si c'est faisable. PHP lui-même ne fournit que des générateurs pour prendre en charge les appels de coroutine. S'il ne s'appuie pas sur des extensions, il ne fournit pas de méthode d'implémentation de programme multithread. Il n'est pas aussi puissant que Java et peut être implémenté en ouvrant des sous-threads.

J'ai l'impression que les sous-threads de Java s'exécutent indépendamment et ne se bloquent pas les uns les autres, donc je me dis que puisque PHP peut implémenter un mécanisme similaire au multi-threading, peut-il parvenir au non-blocage lors de l'appel processus? ?

Après une telle implémentation et réflexion, je suis tombé dans un malentendu au début. C'était un malentendu causé par le blocage de la fonction native PHP

, c'est-à-dire si je veux vraiment atteindre le non-. le blocage ou l'implémentation asynchrone doivent dépendre du langage sous-jacent. sleep()

Plus tard, j'ai découvert une vérité. Puisqu'une certaine méthode ou fonction se bloquera pendant l'exécution, remplacez la méthode actuelle par une méthode personnalisée et rendez-la non bloquante (par rapport à l'ensemble du protocole). de planification, cela ne suffirait-il pas ? Par exemple, j'ai moi-même implémenté l'exécution planifiée ci-dessus.

D'un autre côté, le but de la planification des coroutines elle-même est de découper le processus d'exécution des tâches en morceaux aussi petits que possible, afin de changer rapidement d'exécution et d'atteindre le parallélisme. De ce point de vue, la coroutine doit également être considérée comme une idée de programmation.

Ce qui suit est un exemple de découpe d'un programme en morceaux aussi petits que possible pour l'exécution :


// 一个简单的例子
<?php
function xrange($start, $end, $step = 1) {
 for ($i = $start; $i <= $end; $i += $step) {
 yield $i;
 }
}
 
foreach (xrange(1, 1000000) as $num) {
 echo $num, "\n";
}
Cet exemple consiste à convertir la plage d'utilisation d'origine pour générer un grand tableau d'entiers en exécution fragmentée, ce qui signifie que la valeur spécifiée est obtenue lors du parcours. Du point de vue du code, la consommation de mémoire est très faible par rapport à avant.

Résumé

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn