Entrevistador: La diferencia entre proceso e hilo, yo:? ? ?

1. Proceso

1. La diferencia entre proceso e hilo

Diferencia esencial: el
hilo es la unidad de ejecución , el proceso es la unidad de recursos . Comparta datos entre hilos bajo el mismo proceso.

Por lo tanto, los subprocesos y los procesos tienen las siguientes características diferentes entre la creación, el cambio y la ejecución :

  • Crear: la creación de subprocesos es más rápida. La creación del proceso debe volver a abrir el espacio, el hilo se crea directamente y se puede abrir una pequeña cantidad de recursos.

  • Cambio: los subprocesos son más rápidos, el cambio de proceso requiere un cambio que involucra muchos recursos, los hilos comparten recursos y las velocidades de cambio son más rápidas

  • En ejecución: el proceso se ejecuta más estable, el colapso de un hilo causará el colapso de todo el proceso.

  • Comunicación: la comunicación entre procesos es engorrosa.

    Cuando se crea el proceso, debe indicarle al sistema operativo que abra un nuevo espacio de memoria, que es relativamente lento, y solo se necesita copiar una pequeña cantidad de recursos cuando se crea el hilo. Del mismo modo, la velocidad de conmutación también es más rápida. En base a esto , se pueden derivar los escenarios aplicables a los hilos y procesos mencionados a continuación .

El hilo es decirle al sistema operativo que ejecute un código de tarea (el hilo se crea 100 veces más rápido que el proceso)

2. El proceso de crear un proceso.

llamada al sistema de horquilla

3. Comunicación entre procesos.

1) Tubería (Pipe): las tuberías se pueden usar para la comunicación entre procesos relacionados, lo que permite que un proceso se comunique con otro proceso que tenga un ancestro común.

(2) Tubería con nombre (tubería con nombre): la tubería con nombre supera la restricción de que la tubería no tiene nombre, por lo tanto, además de tener la función de la tubería, también permite la comunicación entre procesos no relacionados. Las canalizaciones con nombre tienen nombres de archivo correspondientes en el sistema de archivos. Las canalizaciones con nombre se crean mediante el comando mkfifo o la llamada al sistema mkfifo.

(3) Señal: la señal es un método de comunicación más complicado. Se utiliza para notificar al proceso de recepción que se ha producido un evento. Además de ser utilizado para la comunicación entre procesos, el proceso también puede enviar una señal al proceso en sí mismo; Linux además de admitir las primeras señales de Unix Además de la función semántica sigal, también admite la función de señal sigaction cuya semántica se ajusta al estándar Posix.1 (de hecho, esta función se basa en BSD. Para lograr un mecanismo de señal confiable, BSD también puede unificar la interfaz externa y reimplementar la función de señal con la función sigaction) .

(4) Cola de mensajes (mensajes): la cola de mensajes es una lista vinculada de mensajes, incluida la cola de mensajes del sistema V de la cola de mensajes Posix. Los procesos con permisos suficientes pueden agregar mensajes a la cola, y los procesos con permisos de lectura pueden leer mensajes de la cola. La cola de mensajes supera las deficiencias de que la señal transporta menos información, la canalización solo puede transportar flujos de bytes sin formato y el tamaño de la memoria intermedia es limitado.

(5) Memoria compartida: permite que múltiples procesos accedan al mismo espacio de memoria y es el formulario IPC más rápido disponible. Está diseñado para la baja eficiencia de otros mecanismos de comunicación. A menudo se usa en combinación con otros mecanismos de comunicación, como los semáforos, para lograr la sincronización y la exclusión mutua entre procesos.

(6) Semáforo (semáforo): se utiliza principalmente como un medio de sincronización entre procesos y entre diferentes hilos del mismo proceso.

(7) Socket (Socket): un mecanismo de comunicación entre procesos más general que se puede utilizar para la comunicación entre procesos entre diferentes máquinas. Originalmente fue desarrollado por la rama BSD del sistema Unix, pero ahora generalmente se puede portar a otros sistemas similares a Unix: las variantes de Linux y System V admiten sockets.

3. Explica el proceso huérfano, el proceso zombie, el sorprendente efecto grupal

Proceso huérfano :
si el proceso padre sale primero y el proceso hijo aún no ha salido, entonces el proceso hijo quedará huérfano al proceso init. Este es el proceso padre del proceso hijo es el proceso init (proceso 1). De hecho, todavía se entiende bien.

Proceso zombie :
si conocemos el estado y la relación de transición del proceso de Linux, deberíamos saber que uno de los tantos estados del proceso es el estado zombie, es decir, el proceso ingresa al estado zombie (zombie) después de que el proceso finaliza, esperando informar al proceso padre para que termine, y luego completar Desaparece. Pero si un proceso se ha terminado, pero su proceso padre aún no ha obtenido su estado, entonces este proceso se llama proceso zombie. El proceso zombie también consumirá ciertos recursos del sistema y también retendrá cierta información de resumen para que el proceso padre lo consulte. El estado del proceso secundario puede proporcionar la información que el proceso primario desea. Una vez que el proceso primario obtiene la información deseada, el proceso zombie finalizará.

Efecto de grupo de choque :
Grupo de choque simplemente significa que múltiples procesos o subprocesos están esperando el mismo evento. Cuando ocurre un evento, todos los subprocesos y procesos serán despertados por el núcleo. Después del despertar, generalmente solo un proceso obtiene el evento y lo procesa, y otros procesos continúan ingresando al estado de espera después de descubrir que el evento falla, lo que reduce el rendimiento del sistema en cierta medida.

https://blog.csdn.net/abhem4170/article/details/101951466

Segundo, el hilo

2.1 Creación de hilos

2.2 Sincronización de hilos y comunicación

Las variables de condición son un método importante de sincronización de subprocesos y generalmente se usan con ** mutexes **, entonces, ¿por qué se usan de esta manera?

Para comprender esto, debe hablar sobre los puntos muertos y las cuatro condiciones para los puntos muertos:

  • Condición mutuamente excluyente: un recurso solo puede ser utilizado por un proceso a la vez
  • Condiciones de solicitud y retención: cuando un proceso se bloquea debido a una solicitud de recursos, conserva los recursos adquiridos
  • Sin condición de privación: los recursos ya adquiridos por el proceso no se privarán por la fuerza antes de ser utilizados
  • Condición de espera cíclica: proceso de formar una relación de recurso de espera circular entre varios procesos.

Mecanismo de sincronización y comunicación:

  • Mutexes
    Mutexes generalmente se usan junto con variables de condición. Bloquee antes de usar recursos y notifique a los hilos bloqueados después de usar

  • Spin lock
    Spin lock es también un bloqueo de mutex en un sentido amplio. Es uno de los métodos de implementación de un bloqueo de mutex. No genera programación de subprocesos, pero intenta adquirir el bloqueo a través del "bucle". La ventaja es que se puede adquirir rápidamente. Bloqueo, la desventaja es que tomará demasiado tiempo de CPU, lo que se llama ocupado esperando.

  • Un bloqueo de lectura-escritura
    solo tiene dos estados en un bloqueo de exclusión mutua: bloqueado y desbloqueado, y en algunos casos la "lectura" se puede realizar simultáneamente sin bloqueo, y el bloqueo debe bloquearse para la lectura, como el mapa en golang Operación

Para acelerar la operación de "lectura" (sin bloqueo), nació el concepto de "bloqueo de lectura-escritura". Tiene tres estados: bloqueado en modo lectura, bloqueado en modo escritura y desbloqueado.

Las reglas son las siguientes:
1. Si hay otros hilos que leen datos, otros hilos pueden realizar operaciones de lectura, pero las operaciones de escritura no están permitidas
2. Si hay otros hilos que escriben datos, otros hilos no tienen permiso para leer y escribir operaciones.

Debido a esta característica, los bloqueos de lectura y escritura pueden tener un mejor rendimiento de concurrencia a frecuencias de lectura más altas.

  • barrera (no sé traducir)

Código

La diferencia entre estos tipos, bienvenido a navegar y discutir

2.1.1 Mutexes y variables de condición

¿Por qué necesitamos agregar un bloqueo mutex para las variables de condición?
https://www.zhihu.com/question/53631897

La diferencia entre unir y separar en subprocesos múltiples

Cada proceso tiene un hilo principal responsable de la ejecución, generalmente el hilo en la función principal.

El subproceso principal finaliza y el subproceso secundario también finaliza.

Entorno de hilo:

El hilo existe en el proceso, y todos los recursos globales en el proceso son visibles para cada hilo interno.

Los recursos globales típicos en el proceso son los siguientes:

1) Área de código: Esto significa que todos los códigos de función visibles en el espacio de proceso actual también son visibles para cada subproceso

2) Área de almacenamiento estático: variables globales, espacio estático

3) Área de almacenamiento dinámico: espacio de almacenamiento dinámico

Recursos locales típicos dentro del hilo:

1) Espacio de pila local: almacene la pila de llamadas de función de este hilo, variables locales dentro de la función, etc.

2) Parte de la variable de registro: el desplazamiento del puntero del siguiente código de ejecución del hilo

Después de que se inicia un proceso, primero se genera un hilo predeterminado. Este hilo generalmente se llama hilo principal. En un programa C / C ++, el hilo principal es el hilo ingresado a través de la función principal. El hilo derivado del hilo principal se convierte en el hilo esclavo y el hilo esclavo También puede tener su propia función de entrada, equivalente a la función principal del hilo principal, esta función la especifica el usuario. Esto se logra al pasar un puntero de función en el constructor de subprocesos.Al especificar la función de entrada de subprocesos, también puede especificar los parámetros de la función de entrada. Al igual que la función principal tiene un requisito de formato fijo, la función de entrada de subprocesos también puede tener un requisito de formato fijo. Los parámetros suelen ser de tipo nulo y el tipo de retorno es diferente según el protocolo. Pthread es nulo y winapi es unsigned int. Y son todas funciones globales.

En el modelo de subprocesos más común, excepto que el subproceso principal es especial, una vez que se crean otros subprocesos, son de igual a igual y no hay una relación jerárquica oculta. El número máximo de subprocesos que cada proceso puede crear está determinado por la implementación específica.

Ya sea en Windows o Posix, la relación predeterminada entre el subproceso principal y el subproceso es: no importa si la ejecución del subproceso se completa o no, una vez que la ejecución del subproceso principal se completa y sale, toda la ejecución del subproceso finalizará. En este momento, todo el proceso finaliza o está inactivo, y algunos subprocesos mantienen un estado en el que finaliza la ejecución pero aún no se destruye, y el proceso debe destruirse después de que se destruyan todos sus subprocesos. La función de subproceso sale después de la ejecución o termina de otras formas extraordinarias. El subproceso ingresa al estado terminado, pero los recursos del sistema asignados al subproceso pueden no liberarse. Puede que no se libere hasta que se reinicie el sistema. El subproceso terminado aún se utiliza como una entidad de subprocesos. Existe en el sistema operativo, cuando destruir, depende de los atributos del hilo. En este caso, el hilo principal y el hilo secundario generalmente definen las siguientes dos relaciones:

1. Unible : en esta relación, el subproceso principal debe realizar la operación de espera explícitamente. Después de que finaliza el subproceso secundario, se completa la operación de espera del subproceso principal, y el subproceso secundario y el subproceso principal se encuentran. En este momento, el subproceso principal continúa realizando la operación de espera El siguiente paso después de eso. El subproceso principal debe cumplir con subprocesos secundarios que pueden encontrarse. La función de espera del objeto de subproceso secundario se llama dentro de la función de subproceso del subproceso principal. Incluso si el subproceso secundario puede completar la ejecución antes del subproceso principal e ingresar al estado de terminación, debe realizar una operación de encuentro. De lo contrario, el sistema nunca destruirá activamente el subproceso y lo asignará al Los recursos del sistema del hilo nunca se liberarán.

2. Separado (separado) : significa que el subproceso secundario no necesita cumplir con el subproceso principal, es decir, está separado. En este caso, una vez que el subproceso secundario ingresa al estado de terminación, este método se usa a menudo en el caso de una gran cantidad de subprocesos, a veces se deja Es difícil o imposible que el subproceso principal espere el final de los subprocesos uno por uno, o deje que el subproceso principal organice la secuencia de espera para el final de cada subproceso, por lo que este método se usa a menudo cuando hay muchos subprocesos concurrentes.

En cualquier momento, un hilo se puede unir o separar. Un hilo que se puede unir puede ser reclamado y eliminado por otros hilos. Antes de ser reclamado por otros hilos, sus recursos de memoria son La pila no se libera. Por el contrario, un subproceso separado no puede ser recuperado o eliminado por otros subprocesos, y sus recursos de memoria son liberados automáticamente por el sistema cuando finaliza.

El estado de separación de subprocesos determina cómo termina un subproceso. De forma predeterminada, el subproceso está en un estado no separado. En este caso, el subproceso original espera a que finalice la creación del subproceso. Solo cuando la función pthread_join vuelve, Se considera que el subproceso creado está terminado y se libera el sistema que ocupa Ziyun, y el subproceso de separación no está esperando otros subprocesos. Cuando finaliza la operación, el subproceso finaliza y los recursos del sistema se liberan inmediatamente.

3. Algunos temas típicos

2.1 ¿Cuándo usar multihilo? ¿Cuándo usar multiproceso?

Las diferencias entre subprocesos y procesos son las siguientes:
1. Es necesario crear y destruir con frecuencia el uso prioritario de subprocesos, porque el proceso de crear y destruir un proceso es muy costoso.

2. La velocidad de cambio de hilo es rápida, por lo que cuando se requiere una gran cantidad de cálculo, el hilo se usa con frecuencia al cambiar, y la operación que consume mucho tiempo usa el hilo para mejorar la respuesta de la aplicación.

3. Debido a que el uso de subprocesos superiores para la eficiencia del sistema de la CPU es más dominante, puede desarrollarse en procesos distribuidos de múltiples máquinas y subprocesos distribuidos de múltiples núcleos;

4. Use subprocesos en operaciones paralelas, como subprocesos concurrentes del lado del servidor de la arquitectura C / S para responder a las solicitudes de los usuarios;

5. Cuando necesita más estabilidad y seguridad, es adecuado para seleccionar procesos, cuando necesita velocidad, es mejor elegir hilos.

Las siguientes conclusiones se pueden extraer a través de 1 \ 2:
uso intensivo de E / S de subprocesos múltiples, porque la E / S implica conmutación frecuente, el uso de subprocesos múltiples es relativamente pequeño y el
uso intensivo de cálculo de multiprocesos, porque el proceso es una unidad de recursos, hay Para CPU de múltiples núcleos, es más rápido participar en múltiples procesos

2.2. Preguntas frecuentes de entrevistas multiproceso en C ++


#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
using namespace std;
mutex m;
condition_variable cond;
int flag=10;
void fun(int num){
    for(int i=0;i<50;i++){
        unique_lock<mutex> lk(m);//A unique lock is an object that manages a mutex object with unique ownership in both states: locked and unlocked.
        while(flag!=num)
            cond.wait(lk);//在调用wait时会执行lk.unlock()
        for(int j=0;j<num;j++)
            cout<<j<<" ";
        cout<<endl;
        flag=(num==10)?100:10;
        cond.notify_one();//被阻塞的线程唤醒后lk.lock()恢复在调用wait前的状态
    }
}

int main(){
    thread child(fun,10);
    fun(100);
    child.join();
    return 0;
}

Referencias

[1] https://www.cnblogs.com/fah936861121/articles/8043187.html

92 artículos originales publicados · Me gusta20 · Visitas 90,000+

Supongo que te gusta

Origin blog.csdn.net/qq_16761099/article/details/97400176
Recomendado
Clasificación