La entrevista de PHP se reunió con la corrutina de swoole del entrevistador durante tres preguntas consecutivas, ¡casi llorando!

¿Qué es un proceso?

El proceso es la instancia de inicio de la aplicación. Recursos de archivos independientes, recursos de datos, espacio de memoria.

¿Qué es un hilo?

El hilo pertenece al proceso y es el ejecutor del programa. Un proceso contiene al menos un hilo principal y puede tener más hilos secundarios. Hay dos estrategias de programación para subprocesos, una es: programación de tiempo compartido y la otra es: programación preventiva.

Mi colonia oficial de pingüinos

¿Qué es una corrutina?

La corrutina es un hilo ligero, la corrutina también es un hilo y la corrutina se ejecuta en el hilo. El usuario cambia manualmente la programación de la corrutina, por lo que también se denomina hilo de espacio de usuario. La creación, conmutación, suspensión y destrucción de la corrutina son todas operaciones de memoria y el consumo es muy bajo. La estrategia de programación de la corrutina es: programación colaborativa.

El principio de la rutina de lana Swoole

  • Debido a que Swoole4 es de un solo subproceso y es multiproceso, solo se ejecuta una corrutina en el mismo proceso al mismo tiempo.

  • El servidor Swoole recibe datos y activa la devolución de llamada onReceive en el proceso de trabajo para generar un Ctrip. Swoole crea un Ctrip correspondiente para cada solicitud. También se puede crear una subcorutina en una corrutina.

  • La corrutina es de un solo subproceso en la implementación subyacente, por lo que solo una corrutina funciona al mismo tiempo y la ejecución de la corrutina es en serie.

  • Por lo tanto, cuando se ejecutan múltiples tareas y múltiples corrutinas, cuando una corrutina se está ejecutando, otras corrutinas dejarán de funcionar. La corrutina actual se bloqueará al realizar operaciones de bloqueo de E / S y el programador subyacente entrará en el bucle de eventos. Cuando hay un evento de finalización de E / S, el planificador subyacente reanuda la ejecución de la corrutina correspondiente al evento. . Por lo tanto, no hay E / S que consuman mucho tiempo para las corrutinas, lo que es muy adecuado para escenarios de E / S concurrentes. (Como se muestra abajo)

Inserte la descripción de la imagen aquí

Proceso de ejecución de corrutinas de Swoole

  • La corrutina no tiene IO y espera la ejecución normal del código PHP, y no se produce ningún cambio de flujo de ejecución.

  • Cuando la corrutina encuentra IO y espera para cambiar inmediatamente el control a la derecha, después de que se complete la IO, cambie el flujo de ejecución de nuevo al punto donde se cortó la corrutina

  • Las corrutinas paralelas se ejecutan en secuencia, igual que la lógica anterior

  • El proceso de ejecución anidado de la corrutina ingresa capa por capa desde el exterior hacia el interior, hasta que ocurre IO, y luego se corta a la corrutina externa, la corrutina principal no esperará al final de la corrutina secundaria

El orden de ejecución de la corrutina

Echemos un vistazo al ejemplo básico:

go(function () {
    
    
    echo "hello go1 \n";
});

echo "hello main \n";

go(function () {
    
    
    echo "hello go2 \n";
});

go()Es el \Co::create()acrónimo utilizado para crear una corrutina, acepta como parámetro callback, el código de callback se ejecuta en esta nueva corrutina.

Observaciones: \Swoole\Coroutinese puede abreviar como\Co

El resultado de la ejecución del código anterior:

root@b98940b00a9b /v/w/c/p/swoole# php co.php
hello go1
hello main
hello go2

El resultado de la ejecución y el orden en el que normalmente escribimos el código parecen ser los mismos. El proceso de ejecución real:

  • Ejecute este código, el sistema inicia un nuevo proceso

  • Encontrado go(), se genera una corrutina en el proceso actual, la salida heelo go1en la corrutina y la corrutina sale

  • El proceso continúa ejecutando el código, salida hello main

  • Genere otra corrutina, envíe la salida heelo go2a la corrutina y salga de la corrutina

Ejecute este código, el sistema inicia un nuevo proceso. Si no entiende esta frase, puede utilizar el siguiente código:

// co.php
<?php

sleep(100);

La implementación y uso del ps auxsistema Process Viewer:

root@b98940b00a9b /v/w/c/p/swoole# php co.php &
⏎
root@b98940b00a9b /v/w/c/p/swoole# ps aux
PID   USER     TIME   COMMAND
    1 root       0:00 php -a
   10 root       0:00 sh
   19 root       0:01 fish
  749 root       0:00 php co.php
  760 root       0:00 ps aux
⏎

Cambiémoslo un poco y experimentemos la programación de la corrutina:

use Co;

go(function () {
    
    
    Co::sleep(1); // 只新增了一行代码
    echo "hello go1 \n";
});

echo "hello main \n";

go(function () {
    
    
    echo "hello go2 \n";
});

\Co::sleep()Características y funciones sleep()similares, pero simula la espera de IO (IO entrará en detalles más adelante) la implementación de los resultados son los siguientes:

root@b98940b00a9b /v/w/c/p/swoole# php co.php
hello main
hello go2
hello go1

¿Por qué no se ejecuta secuencialmente? El proceso de ejecución real:

  • Ejecute este código, el sistema inicia un nuevo proceso
  • Encontrado go(), se genera una corrutina en el proceso actual
  • Coroutine encontró bloqueo de E / S (aquí es Co::sleep()para simular la espera de E / S), las corrutinas ceden el control, la cola de programación para ingresar a la corrutina
  • El proceso continúa ejecutándose hacia abajo, salida hello main
  • Ejecute la siguiente corrutina, salida hello go2
  • La corrutina anterior está lista, continuar ejecución, salida hello go1

En este punto, ya se puede ver la relación entre la corrutina y el proceso en swoole, así como la programación de la corrutina, cambiemos el programa anterior:

go(function () {
    
    
    Co::sleep(1);
    echo "hello go1 \n";
});

echo "hello main \n";

go(function () {
    
    
    Co::sleep(1);
    echo "hello go2 \n";
});

Creo que ya sabes cómo se ve la salida:

root@b98940b00a9b /v/w/c/p/swoole# php co.php
hello main
hello go1
hello go2
⏎

¿Dónde está la corrutina rápida? Reducir la pérdida de rendimiento causada por el bloqueo de E / S

Es posible que escuche la razón más común para usar corrutinas, que puede ser que las corrutinas son rápidas. ¿Por qué el código que parece estar escrito de manera similar en tiempos normales debería ser más rápido? Una razón común es que puede crear muchas corrutinas para ejecutar Tarea, tan rápido. Esta afirmación es correcta, pero aún permanece en la superficie.

En primer lugar, las tareas informáticas generales se dividen en dos tipos:

  • Uso intensivo de CPU, como cálculos científicos como suma, resta, multiplicación y división
  • IO intensivo, como solicitudes de red, lectura y escritura de archivos, etc.

En segundo lugar, hay 2 conceptos relacionados con el alto rendimiento:

  • Paralelo: al mismo tiempo, la misma CPU solo puede realizar la misma tarea. Para realizar varias tareas al mismo tiempo, se requieren varias CPU.
  • Simultaneidad: dado que la CPU cambia las tareas muy rápido, alcanzando el límite que los humanos pueden percibir, habrá la ilusión de que se ejecutan muchas tareas al mismo tiempo.

Sabiendo esto, veamos la corrutina nuevamente. La corrutina es adecuada para aplicaciones intensivas de IO, porque la corrutina se programa automáticamente cuando se bloquea IO, lo que reduce la pérdida de tiempo causada por el bloqueo de IO.

Podemos comparar las siguientes tres piezas de código:

  • Versión normal: realiza 4 tareas
$n = 4;
for ($i = 0; $i < $n; $i++) {
    
    
    sleep(1);
    echo microtime(true) . ": hello $i \n";
};
echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.php
1528965075.4608: hello 0
1528965076.461: hello 1
1528965077.4613: hello 2
1528965078.4616: hello 3
hello main
real    0m 4.02s
user    0m 0.01s
sys     0m 0.00s
⏎
  • Versión de una sola corrutina:
$n = 4;
go(function () use ($n) {
    
    
    for ($i = 0; $i < $n; $i++) {
    
    
        Co::sleep(1);
        echo microtime(true) . ": hello $i \n";
    };
});
echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.php
hello main
1528965150.4834: hello 0
1528965151.4846: hello 1
1528965152.4859: hello 2
1528965153.4872: hello 3
real    0m 4.03s
user    0m 0.00s
sys     0m 0.02s
⏎
  • Versión multicorutina: presencia el momento del milagro
$n = 4;
for ($i = 0; $i < $n; $i++) {
    
    
    go(function () use ($i) {
    
    
        Co::sleep(1);
        echo microtime(true) . ": hello $i \n";
    });
};
echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.php
hello main
1528965245.5491: hello 0
1528965245.5498: hello 3
1528965245.5502: hello 2
1528965245.5506: hello 1
real    0m 1.02s
user    0m 0.01s
sys     0m 0.00s
⏎

¿Por qué hay una diferencia tan grande en el tiempo?

  • La escritura ordinaria encontrará una pérdida de rendimiento causada por el bloqueo de E / S

  • Corrutina única: aunque el bloqueo de E / S activó la programación de corrutinas, actualmente solo hay una corrutina y la corrutina actual todavía se ejecuta después de la programación

  • Multicorutina: Realmente ejerce las ventajas de la corrutina, la programación se produce cuando IO está bloqueado y reanuda el funcionamiento cuando IO está listo

Modificaremos ligeramente la versión de varias corrutinas:

  • Multi-coroutine versión 2: CPU intensiva
$n = 4;
for ($i = 0; $i < $n; $i++) {
    
    
    go(function () use ($i) {
    
    
        // Co::sleep(1);
        sleep(1);
        echo microtime(true) . ": hello $i \n";
    });
};
echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.php
1528965743.4327: hello 0
1528965744.4331: hello 1
1528965745.4337: hello 2
1528965746.4342: hello 3
hello main
real    0m 4.02s
user    0m 0.01s
sys     0m 0.00s
⏎

Recién Co::sleep()cambiado sleep(), y el tiempo es casi el mismo que en la versión normal:

  • sleep() Puede considerarse como una tarea intensiva en CPU, que no provocará la programación de la corrutina.

  • Co::sleep()La simulación son tareas intensivas en IO, que activarán la programación de corrutinas
    , por lo que las corrutinas son adecuadas para aplicaciones intensivas en IO.

Aquí hay otro conjunto de ejemplos comparativos: usando redis

// 同步版, redis使用时会有 IO 阻塞
$cnt = 2000;
for ($i = 0; $i < $cnt; $i++) {
    
    
    $redis = new \Redis();
    $redis->connect('redis');
    $redis->auth('123');
    $key = $redis->get('key');
}

// 单协程版: 只有一个协程, 并没有使用到协程调度减少 IO 阻塞
go(function () use ($cnt) {
    
    
    for ($i = 0; $i < $cnt; $i++) {
    
    
        $redis = new Co\Redis();
        $redis->connect('redis', 6379);
        $redis->auth('123');
        $redis->get('key');
    }
});

// 多协程版, 真正使用到协程调度带来的 IO 阻塞时的调度
for ($i = 0; $i < $cnt; $i++) {
    
    
    go(function () {
    
    
        $redis = new Co\Redis();
        $redis->connect('redis', 6379);
        $redis->auth('123');
        $redis->get('key');
    });
}

Comparación de rendimiento:

# 多协程版
root@0124f915c976 /v/w/c/p/swoole# time php co.php
real    0m 0.54s
user    0m 0.04s
sys     0m 0.23s
⏎

# 同步版
root@0124f915c976 /v/w/c/p/swoole# time php co.php
real    0m 1.48s
user    0m 0.17s
sys     0m 0.57s
⏎

Comparación de la co-rutina swoole y la co-rutina: proceso único frente a múltiples hilos

He estado en contacto con el codificador de la corrutina go, y el contacto inicial con la corrutina swoole será un poco confuso.

package main

import (
    "fmt"
    "time"
)

func main() {
    
    
    go func() {
    
    
        fmt.Println("hello go")
    }()

    fmt.Println("hello main")

    time.Sleep(time.Second)
}
> 14:11 src $ go run test.go
hello main
hello go

Simplemente vaya a escribir el codificador de corrutinas, al escribir este código se le dirá que no lo olvide time.Sleep(time.Second), o no vea la salida hello go, y en segundo lugar, hello gocon hello mainel orden y también en la corrutina de swoole no es lo mismo.

La razón es que swoole and go tienen diferentes modelos para implementar la programación de corrutinas.

El proceso de ejecución del código go anterior:

  • Ejecute go code, el sistema inicia un nuevo proceso
  • Encuentra package mainy luego ejecutafunc mian()
  • Cuando encuentre una corrutina, entréguela al programador de corrutinas para que la ejecute
  • Continuar ejecutando hacia abajo, salida hello main
  • Si no lo agrega time.Sleep(time.Second), la función principal se ejecuta, el programa finaliza y el proceso sale, lo que hace que la corrutina en la programación también termine

La corrutina en marcha, el modelo MPG utilizado:

  • M se refiere a máquina, una M está directamente asociada con un hilo del kernel
  • P se refiere al procesador, que representa el contexto requerido por M, y también es un procesador que procesa la lógica del código a nivel de usuario.
  • G se refiere a Goroutine, que es esencialmente un hilo ligero

Modelo de MPG

Y la programación de gorutinas en swoole utiliza un solo modelo de proceso, todas las gorutinas se programan en el proceso actual y los beneficios de un solo proceso también son obvios: simple / sin bloqueos / alto rendimiento.

Ya sea el modelo MPG de go o el modelo de proceso único de swoole, es una implementación de la teoría CSP.

El método de comunicación CSP ya estaba disponible en un artículo en 1985. Para aquellos que realizan investigación teórica, puede ser difícil mejorar sin las audaces suposiciones que se pueden hacer con varios años, diez o incluso décadas de anticipación.

Presta atención, no te pierdas

Muy bien, todos, lo anterior es todo el contenido de este artículo. Las personas que pueden ver aquí son todos talentos . Como dije antes, hay muchos puntos técnicos en PHP, porque hay demasiados, es realmente imposible de escribir, y no leerás demasiado después de escribirlo, así que lo organizaré en PDF y documentos aquí, si es necesario. lata

Haga clic para ingresar el código secreto: PHP + 「Plataforma」

Inserte la descripción de la imagen aquí

Inserte la descripción de la imagen aquí


Para obtener más contenido de aprendizaje, visite el excelente catálogo de tutoriales de arquitecto PHP de [Comparative Standard Factory], siempre que pueda leerlo para asegurarse de que el salario aumentará un paso (actualización continua)

El contenido anterior espera poder ayudarte . Muchos PHPers siempre encuentran algunos problemas y cuellos de botella cuando están avanzados. No hay sentido de dirección cuando escriben demasiado código comercial. No sé por dónde empezar a mejorar. He compilado información sobre esto, incluyendo Pero no se limita a: arquitectura distribuida, alta escalabilidad, alto rendimiento, alta concurrencia, ajuste del rendimiento del servidor, TP6, laravel, YII2, Redis, Swoole, Swoft, Kafka, optimización de Mysql, scripts de shell, Docker, microservicios, Nginx, etc. Muchos puntos de conocimiento, productos secos avanzados avanzados, se pueden compartir con todos de forma gratuita, y aquellos que lo necesiten pueden unirse a mi grupo de intercambio de tecnología PHP

Supongo que te gusta

Origin blog.csdn.net/weixin_49163826/article/details/108870894
Recomendado
Clasificación