Problema de multiproceso de Linux c ++

Debido a problemas del proyecto, recientemente se ha involucrado algo de conocimiento relacionado con los hilos, así que resumamos el progreso de todos.

En el modelo Unix tradicional, cuando un proceso necesita ser ejecutado por otra entidad, el proceso bifurca un proceso hijo y deja que el proceso hijo lo maneje. La mayoría de los programas de servidor de red en Unix están escritos de esta manera, es decir, el proceso padre acepta la conexión, deriva el proceso hijo y el proceso hijo maneja la interacción con el cliente.

Aunque este modelo se ha utilizado bien durante muchos años, existen algunos problemas con las horquillas:

  • El tenedor es caro. La imagen de la memoria se debe copiar del proceso principal al proceso secundario, y todas las palabras descriptivas se deben copiar en el proceso secundario, etc. En la actualidad, algunas implementaciones de Unix utilizan una tecnología llamada copia en escritura, que puede evitar la copia en escritura del espacio de datos del proceso padre en el proceso hijo. A pesar de esta técnica de optimización, la bifurcación sigue siendo cara.
  • 2. Después de bifurcar el proceso hijo, debe utilizar la comunicación entre procesos (IPC) para transferir información entre los procesos padre e hijo. La información antes de Fork es fácil de pasar, porque el proceso hijo tiene una copia del espacio de datos del proceso padre y todas las palabras de descripción desde el principio. Pero devolver información del proceso hijo al proceso padre requiere más trabajo.

Los hilos ayudan a resolver estos dos problemas. Los subprocesos a veces se denominan procesos ligeros porque son "ligeros" que los procesos En general, crear un subproceso es de 10 a 100 veces más rápido que crear un proceso.

    Todos los subprocesos de un proceso comparten la misma memoria global, lo que facilita que los subprocesos compartan información, pero esta simplicidad también trae problemas de sincronización.

Todos los subprocesos de un proceso no solo comparten variables globales, sino que también comparten: instrucciones del proceso, la mayoría de los datos, archivos abiertos (como palabras de descripción), manejadores de señales y manejo de señales, directorio de trabajo actual, ID de usuario e ID de grupo. Pero cada hilo tiene su propio ID de hilo, conjunto de registros (incluido el contador de programa y el puntero de pila), pila (que se usa para almacenar variables locales y direcciones de retorno), error, máscara de señal y prioridad. La programación de subprocesos en Linux se ajusta al estándar Posix.1 y se llama Pthreads. Todas las funciones de pthread comienzan con pthread_. Antes de llamarlos, debe incluir el archivo de encabezado pthread.h, una biblioteca de funciones libpthread.a.

Atributos del hilo


  • Estructura y archivo de encabezado

La estructura del atributo del hilo es pthread_attr_t, que se define en el archivo de encabezado /usr/include/pthread.h. La función de inicialización es pthread_attr_init, esta función debe llamarse antes que la función pthread_create. Los objetos de atributo incluyen principalmente si se debe vincular, si se debe separar, la dirección de la pila, el tamaño de la pila y la prioridad. Los atributos predeterminados son no vinculantes, sin separación, una pila predeterminada de 1 M y la misma prioridad que el proceso principal.

  • Enlace de hilo

La función para establecer el estado de enlace del hilo es pthread_attr_setscope. Tiene dos parámetros. El primero es un puntero a la estructura del atributo y el segundo es el tipo de enlace. Tiene dos valores: PTHREAD_SCOPE_SYSTEM (obligado) y PTHREAD_SCOPE_PROCESS ( Sin consolidar). El siguiente código crea un hilo enlazado.  

    Proceso de peso ligero (LWP: proceso de peso ligero). Un proceso ligero puede entenderse como un hilo del núcleo, que se encuentra entre la capa del usuario y la capa del sistema. La asignación de recursos de subprocesos y el control de subprocesos por parte del sistema se realizan mediante procesos ligeros, y un proceso ligero puede controlar uno o más subprocesos. .     

  • Separación de hilos

El estado separado de un hilo determina cómo termina un hilo. El hilo original espera el final del hilo creado. Solo cuando la función pthread_join () regresa, el hilo creado puede considerarse terminado y puede liberar los recursos del sistema ocupados por sí mismo. El hilo separado no es así. No es esperado por otros hilos. Cuando se sobrepasa, el hilo termina y libera inmediatamente los recursos del sistema. El programador debe elegir el estado de separación apropiado según sus necesidades. La función para establecer el estado de separación del hilo es pthread_attr_setdetachstate (pthread_attr_t * attr, int detachstate). El segundo parámetro puede ser PTHREAD_CREATE_DETACHED (hilo separado) y PTHREAD _CREATE_JOINABLE (hilo no separado

  • Tarea prioritaria

La configuración de prioridad generalmente se obtiene primero y luego se modifica. Para modificar la prioridad del hilo, es necesario prestar atención al tipo de proceso creado.

SCHED_OTHER representa el proceso normal creado.
SCHED_FIFO && SCHED_RR representa el hilo creado cuya prioridad se puede modificar, y la prioridad alta prevalecerá sobre la prioridad baja. 
En el primero, durante la operación de alta prioridad, la prioridad baja no se puede adelantar y solo puede esperar hasta que la prioridad alta se cierre activamente; para la misma prioridad, el primer proceso en ejecución siempre ocupará la CPU, y solo después de que el primer proceso en ejecución se cierre activamente, los procesos posteriores Obtenga el intervalo de tiempo.
En este último, la prioridad alta se adelantará a la prioridad baja. Durante la operación de alta prioridad, la prioridad baja no puede adelantarse, y solo puede esperar hasta que la prioridad alta salga activamente; para procesos de la misma prioridad, cada proceso se ejecutará durante un cierto período de tiempo sucesivamente. Cortar (alrededor de 100 ms).

/*
 * ThreadTest.cpp
 *
 *  Created on: Jul 10, 2019
 *  Author: DY
 */
//#include "../core/Thread.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
using namespace std;
extern "C" void* Thread2 (void*);
extern "C" void* Thread1 (void*);
void* Thread1(void*)
{
    sleep(1);
    int i,j;
    int policy;
    struct sched_param param;
    pthread_getschedparam(pthread_self(),&policy,&param);
    if(policy == SCHED_OTHER)  printf("SCHED_OTHER\n");
    if(policy == SCHED_RR);    printf("SCHED_RR 1 \n");
    for(i=1;i<10;i++){
        for(j=1;j<500;j++){}
        printf("thread 1\n");}
}
void* Thread2(void*)
{
    sleep(1);
    int i,j;
    int policy;
    struct sched_param param;
    pthread_getschedparam(pthread_self(),&policy,&param);
    if(policy == SCHED_OTHER)  printf("SCHED_OTHER\n");
    if(policy == SCHED_RR);    printf("SCHED_RR\n");

    for(i=1;i<10;i++){
        for(j=1;j<500;j++){}
        printf("thread 2\n");}
}
int main(){
    pthread_t p1,p2;
    struct sched_param param;
    pthread_attr_t attr,attr1;

    //初始化
    pthread_attr_init(&attr);
    pthread_attr_init(&attr1);
    //设置线程模式
    pthread_attr_setschedpolicy(&attr,SCHED_RR);
    pthread_attr_setschedpolicy(&attr1,SCHED_RR);
    //获取线程属性并打印
    pthread_attr_getschedparam(&attr, &param);
    printf("%d\n",param.__sched_priority);
    pthread_attr_getschedparam(&attr1, &param);
    printf("%d\n",param.__sched_priority);

    //线程的优先级[1,99]值越大优先级越高
    param.sched_priority = 50;
    pthread_attr_setschedparam(&attr,&param);
    //该函数的意思为创建的进程优先级是否同父进程相同
    //pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED);
    //创建线程
    pthread_create(&p1,&attr,Thread1,NULL);
    pthread_attr_getschedparam(&attr, &param);
    printf("%d\n",param.__sched_priority);

    param.sched_priority = 20;
    pthread_attr_setschedparam(&attr1,&param);
    //pthread_attr_setinheritsched(&attr1,PTHREAD_EXPLICIT_SCHED);
    pthread_create(&p2,&attr1,Thread2,NULL);
    pthread_attr_getschedparam(&attr1, &param);
    printf("%d\n",param.__sched_priority);

    //等待线程退出并销毁
    pthread_join(p2,NULL);
    pthread_join(p1,NULL);
    pthread_attr_destroy(&attr);
    pthread_attr_destroy(&attr1);
    //pthread_exit(NULL);
    return 0;
}

Hay un problema durante la ejecución del programa. Se informará el siguiente error durante la compilación de g ++: error: conversión no válida de'void (*) (void *) 'a'void * (*) (void *)' [-fpermissive]. La razón puede ser que el compilador de lenguaje C permite la conversión implícita de un puntero general a un puntero de cualquier tipo, lo que no está permitido en C ++. La razón es que el tercer parámetro en pthread_create () es cargar una función, esta función tiene un parámetro que se puede pasar y devuelve un puntero general.
Por lo tanto, la solución al error anterior: 1) La función hilo se define como hilo vacío (void *), y el lugar de llamada se escribe como: int ret = pthread_create (& id, & attr, (viod *) & hilo, NULL); 2) Función hilo Definido como void * thread1 (void *), el lugar de llamada es: int ret = pthead_create (& id, & attr, thread1, NULL). Luego compila.


Creación de hilo

//创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。
//函数原型:
int pthread_create(pthread_t *restrict tidp,
                    const pthread_attr_t *restrict attr,    
                    void *(*start_rtn)(void),void *restrict arg);
//形式参数:
//pthread_t *restrict tidp 要创建的线程的线程id指针,创建成功后,指向的内存存放创建线程的id;
//const pthread_attr_t *restrict attr一个不透明的属性对象,具体见上文属性的创建;
//void* (start_rtn)(void)返回值是void类型的指针函数;线程函数的起始地址,线程一旦创建立马执行;
//void* restrict arg start_rtn的形参;
//返回值:若是成功建立线程返回0,否则返回错误的编号。  
int ret = pthread_create (thread, attr, start_routine, arg);

Hilo colgante

函数 原型 : int  pthread_join (pthread_t hilo, void ** value_ptr);

La descripción del parámetro es la siguiente: número de subproceso del subproceso que espera salir del subproceso; value_ptr el valor de retorno del subproceso que sale. La función de esta función hace que el hilo actual se suspenda, esperando a que vuelva otro hilo antes de continuar con la ejecución. Es decir, cuando el programa se ejecuta en este lugar, el programa se detendrá primero y luego esperará a que el hilo cuyo identificador de hilo sea hilo regrese, y luego el programa se ejecutará de forma intermitente.

Terminación del hilo

Prototipo de función: int pthread_exit (void * rval_pt)

La descripción de los parámetros es la siguiente:

Aquí, se pthread_exit usa para salir explícitamente de un hilo. Normalmente, la función pthread_exit () se llama cuando el hilo no necesita continuar existiendo después de terminar su trabajo. Si main () termina antes que el hilo que creó y sale a través de pthread_exit (), otros hilos continuarán ejecutándose. De lo contrario, se cerrarán automáticamente al final de main ().

 

Supongo que te gusta

Origin blog.csdn.net/Ding86341631/article/details/95244721
Recomendado
Clasificación