"Explicación detallada y aplicación del grupo de subprocesos"

Introducción

El grupo de subprocesos es una tecnología de subprocesos múltiples que se utiliza para utilizar de manera efectiva el procesador de múltiples núcleos de la computadora para mejorar el rendimiento y la eficiencia del programa. El grupo de subprocesos puede administrar y programar múltiples subprocesos y asignar tareas a subprocesos inactivos para su ejecución, evitando así la sobrecarga de crear y destruir subprocesos repetidamente y evitar el desperdicio de recursos del sistema causado por demasiados subprocesos.
El grupo de subprocesos consta de un administrador del grupo de subprocesos, subprocesos de trabajo y colas de tareas. Bajo el control del administrador, se asignan múltiples tareas a los subprocesos de trabajo para su procesamiento, mejorando así el rendimiento y la eficiencia del programa.
El grupo de subprocesos CPP es una tecnología avanzada para realizar programación de subprocesos múltiples, que puede mejorar la eficiencia de ejecución de programas en entornos de alta concurrencia.

considerar

Si todas las tareas se ejecutan infinitamente, el número limitado de subprocesos en el grupo de subprocesos evitará que las tareas se procesen a tiempo, lo que reducirá la velocidad de respuesta y el rendimiento del programa.
En este caso, puede considerar aumentar la cantidad de subprocesos en el grupo de subprocesos para aumentar las capacidades de procesamiento concurrente del programa. Además, se pueden adoptar medidas de procesamiento especiales para tareas ejecutadas infinitamente, como interrupciones programadas o ejecución por lotes, para evitar que las tareas ocupen recursos de subprocesos durante mucho tiempo.
Cabe señalar que, aunque aumentar la cantidad de subprocesos en el grupo de subprocesos puede mejorar las capacidades de procesamiento concurrente del programa, también provocará un cierto consumo de recursos del sistema. Por lo tanto, al configurar el tamaño del grupo de subprocesos, debe considerar de manera integral los recursos disponibles del sistema y las necesidades comerciales reales para lograr un rendimiento y una eficiencia óptimos.

Detalles completos del código

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <functional>

class ThreadPool {
    
    
public:
    ThreadPool(size_t threadCount) : m_stop(false) {
    
    
        for (size_t i = 0; i < threadCount; ++i) {
    
    
            m_threads.emplace_back([this]() {
    
     // 利用 lambda 表达式创建线程
                while(true) {
    
    
                    std::unique_lock<std::mutex> lock(m_mutex);
                    m_cv.wait(lock, [this](){
    
     return m_stop || !m_tasks.empty(); }); // 等待任务或终止信号
                    if (m_stop && m_tasks.empty()) return;`在这里插入代码片`

                    std::function<void()> task = std::move(m_tasks.front()); // 取出任务
                    m_tasks.pop();
                    lock.unlock();
                    
                    task(); // 执行任务
                }
            });
        }
    }
    
    ~ThreadPool() {
    
    
        {
    
    
            std::unique_lock<std::mutex> lock(m_mutex);
            m_stop = true;
        }
        m_cv.notify_all();

        for (auto& thread : m_threads) {
    
    
            thread.join();
        }
    }

    template<class F, class... Args>
    void addTask(F&& f, Args&&... args) {
    
    
        std::function<void()> task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
        {
    
    
            std::unique_lock<std::mutex> lock(m_mutex);
            m_tasks.emplace(std::move(task));
        }
        m_cv.notify_one();
    }
    private:
    std::vector<std::thread> m_threads;
    std::queue<std::function<void()>> m_tasks;
    std::mutex m_mutex;
    std::condition_variable m_cv;
    bool m_stop;
    }

Este código implementa un grupo de subprocesos simple con las siguientes características:
● El número de subprocesos en el grupo de subprocesos se especifica mediante el parámetro del constructor;
● Admite la adición de tareas, y las tareas son objetos invocables;
● El grupo de subprocesos esperará a que se completen todas las tareas. se ejecuta cuando se destruye Completa y detiene todos los subprocesos;
● El grupo de subprocesos admite múltiples subprocesos para llamar simultáneamente al método addTask, y el grupo de subprocesos utiliza mutex y variables de condición para lograr la sincronización de subprocesos.
Ejemplo de uso:

void foo(int n) {
    
    
    std::cout << "Task " << n << " is running in thread " << std::this_thread::get_id() << std::endl;
}

int main() {
    
    
    ThreadPool pool(4);
    for (int i = 0; i < 10; ++i) {
    
    
        pool.addTask(foo, i);
    }
    return 0;
}

Este ejemplo crea un grupo de subprocesos y agrega 10 tareas, cada tarea es imprimir un fragmento de texto. Se puede ver que se asignan diferentes tareas a diferentes subprocesos para su ejecución.

Explicar con detalle

El grupo de subprocesos es un modelo de programación de subprocesos múltiples de uso común que asigna múltiples tareas a un número fijo de subprocesos para su ejecución, evitando la sobrecarga de la creación y destrucción frecuente de subprocesos y también puede mejorar en gran medida el rendimiento y la eficiencia del programa.

Constructor de grupo de subprocesos

En el constructor, debemos especificar la cantidad de subprocesos en el grupo de subprocesos. Usamos plantillas variadas en C++ 11 para admitir diferentes tipos de objetos invocables. En el constructor, inicializamos la matriz de subprocesos y usamos expresiones lambda para crear funciones de ejecución para cada subproceso. El bucle while en la expresión lambda siempre esperará a que se ejecute una tarea en la cola de tareas y también esperará una señal de si el grupo de subprocesos ha finalizado.

ThreadPool(size_t threadCount) : m_stop(false) {
    
    
    for (size_t i = 0; i < threadCount; ++i) {
    
    
        m_threads.emplace_back([this]() {
    
     // 利用 lambda 表达式创建线程
            while(true) {
    
    
                std::unique_lock<std::mutex> lock(m_mutex);
                m_cv.wait(lock, [this](){
    
     return m_stop || !m_tasks.empty(); }); // 等待任务或终止信号
                if (m_stop && m_tasks.empty()) return;

                std::function<void()> task = std::move(m_tasks.front()); // 取出任务
                m_tasks.pop();
                lock.unlock();
                
                task(); // 执行任务
            }
        });
    }
}

Destructor de grupo de subprocesos

En el destructor, debemos llamar al método join() para esperar a que todos los subprocesos salgan y liberen recursos. Al mismo tiempo, también necesitamos establecer la señal m_stop en verdadero para que la función de ejecución del grupo de subprocesos pueda salir cuando la cola de tareas esté vacía.

~ThreadPool() {
    
    
    {
    
    
        std::unique_lock<std::mutex> lock(m_mutex);
        m_stop = true;
    }
    m_cv.notify_all();

    for (auto& thread : m_threads) {
    
    
        thread.join();
    }
}

Agregar función de tarea

La función addTask() es una función que agrega tareas al grupo de subprocesos. Utiliza plantillas variadas para admitir diferentes tipos de objetos invocables. Primero necesita convertir el objeto invocable en una tarea de tipo función <void()>, luego agregar la tarea a la cola de tareas y notificar a un hilo en espera para ejecutar la tarea.

1template<class F, class... Args>
2void addTask(F&& f, Args&&... args) {
    
    
3    std::function<void()> task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
4    {
    
    
5        std::unique_lock<std::mutex> lock(m_mutex);
6        m_tasks.emplace(std::move(task));
7    }
8    m_cv.notify_one();
9}

Cola de tareas y mecanismo de sincronización.

La cola de tareas y el mecanismo de sincronización son el núcleo de la implementación del grupo de subprocesos. En la cola de tareas, usamos el tipo std::queue<std::function<void()>> para almacenar tareas, donde std::function<void()> representa el tipo de cualquier objeto invocable. Dado que varios subprocesos compartirán la cola de tareas, necesitamos usar un mutex (m_mutex) y una variable de condición (m_cv) para lograr la sincronización de subprocesos.
1std::queue<std::function<void()>> m_tasks;
2std::mutex m_mutex;
3std::condition_variable m_cv;
En la función de ejecución, utilizamos el método wait() de la variable de condición m_cv para esperar el cola de tareas Hay tareas que realizar. Cuando se agrega una nueva tarea o se finaliza el grupo de subprocesos, utilizamos el método notify_one() para activar el subproceso en espera. Al mismo tiempo, cuando ejecutamos una tarea, primero debemos eliminar la tarea de la cola de tareas para evitar que varios subprocesos ejecuten la misma tarea al mismo tiempo.

std::unique_lock<std::mutex> lock(m_mutex);
m_cv.wait(lock, [this](){
    
     return m_stop || !m_tasks.empty(); }); // 等待任务或终止信号
if (m_stop && m_tasks.empty()) return;

Un contenedor para objetos invocables

Al agregar tareas, necesitamos envolver el objeto invocable para que diferentes tipos de objetos invocables se puedan convertir en tareas de tipo función <void()>. Aquí usamos el método std::bind() para implementar el enlace de objetos invocables, y usamos std::forward(f) y std::forward(args)... para reenviar parámetros.

template<class F, class... Args>
void addTask(F&& f, Args&&... args) {
    
    
    std::function<void()> task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
    {
    
    
        std::unique_lock<std::mutex> lock(m_mutex);
        m_tasks.emplace(std::move(task));
    }
    m_cv.notify_one();
}

Resumir

El código anterior implementa un grupo de subprocesos simple, que puede ayudar a los desarrolladores a hacer un mejor uso de la CPU multinúcleo de la computadora para mejorar el rendimiento y la eficiencia del programa. La implementación del grupo de subprocesos debe prestar atención al problema de la sincronización de subprocesos, que se puede lograr fácilmente utilizando std::mutex y std::condition_variable. Al mismo tiempo, preste atención a esperar a que se completen todas las tareas en el destructor para evitar fugas de recursos.

Supongo que te gusta

Origin blog.csdn.net/yiyu20180729/article/details/130730565
Recomendado
Clasificación