Subprocesos múltiples y grupo de subprocesos

ilustrar

Los subprocesos múltiples y los grupos de subprocesos son conceptos comúnmente utilizados en la programación concurrente. A continuación se ofrece una breve explicación de los subprocesos múltiples y los grupos de subprocesos:

Subprocesos múltiples:
subprocesos múltiples se refiere a la ejecución simultánea de múltiples subprocesos (rutas de ejecución independientes) en un programa. Se pueden ejecutar varios subprocesos en paralelo, lo que aumenta la simultaneidad y la eficiencia del programa. Cada hilo tiene un contador de programa independiente, pila, variables locales, etc.

Ventajas del subproceso múltiple:

Mejore la capacidad de respuesta del programa y maneje múltiples tareas al mismo tiempo.
Aproveche al máximo la potencia informática de los procesadores multinúcleo.
Se puede evitar el bloqueo tanto como sea posible y se puede mejorar la eficiencia del programa.
Pero también existen algunos desafíos y consideraciones con el subproceso múltiple:

Sincronización de subprocesos: en un entorno de subprocesos múltiples, varios subprocesos pueden competir por recursos compartidos y deben sincronizarse para evitar la competencia y la inconsistencia de los datos.
Interbloqueo y bloqueo en vivo: las operaciones de sincronización inadecuadas pueden causar un bloqueo en vivo o un bloqueo en vivo entre subprocesos, lo que impide que el programa continúe ejecutándose.
Grupo de subprocesos:
el grupo de subprocesos es un mecanismo para gestionar y reutilizar subprocesos. Proporciona reutilización y gestión de subprocesos mediante la creación previa de un grupo de subprocesos y la asignación de tareas a los subprocesos. Los grupos de subprocesos pueden aumentar la eficiencia de la creación y destrucción de subprocesos y limitar la cantidad de subprocesos que se ejecutan simultáneamente para evitar la competencia de recursos y una sobrecarga excesiva.

Ventajas del grupo de subprocesos:

Mejore la eficiencia de la creación y destrucción de subprocesos y reduzca la sobrecarga de subprocesos.
Controle la cantidad de subprocesos que se ejecutan simultáneamente para evitar el uso excesivo de recursos del sistema.
Proporciona una cola de tareas, que puede programar subprocesos para ejecutar tareas de acuerdo con ciertas estrategias.
Las implementaciones de grupos de subprocesos comunes incluyen ThreadPoolExecutor en Java y ThreadPool en C#.

El grupo de subprocesos es una herramienta para administrar y controlar subprocesos en un entorno de subprocesos múltiples, que puede simplificar la complejidad de la programación concurrente. Al utilizar un grupo de subprocesos, puede utilizar de manera eficiente los recursos de subprocesos y proporcionar control sobre la programación de tareas y la gestión de subprocesos. Al mismo tiempo, el uso razonable de mecanismos de bloqueo y sincronización de subprocesos puede evitar posibles problemas de concurrencia.

Código para implementar subprocesos múltiples y grupo de subprocesos

En C++, puede utilizar thread y ThreadPool en la biblioteca estándar para implementar grupos de subprocesos y subprocesos múltiples. Aquí hay un código de ejemplo simple:

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

// 多线程示例
void threadFunction(int id) {
    
    
    std::cout << "Thread " << id << " is running" << std::endl;
}

int main() {
    
    
    std::vector<std::thread> threads;
    int numThreads = 5;

    // 创建多个线程
    for (int i = 0; i < numThreads; ++i) {
    
    
        threads.emplace_back(threadFunction, i);
    }

    // 等待线程结束
    for (auto& thread : threads) {
    
    
        thread.join();
    }

    return 0;
}

En el código anterior, usamos std::thread para crear múltiples subprocesos, cada subproceso ejecuta la función threadFunction. A través de la función de unión, esperamos a que finalicen todos los subprocesos antes de continuar con la ejecución.

A continuación se muestra un ejemplo de implementación de un grupo de subprocesos:

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

class ThreadPool {
    
    
public:
    ThreadPool(int numThreads) : stop(false) {
    
    
        for (int i = 0; i < numThreads; ++i) {
    
    
            threads.emplace_back([this]() {
    
    
                while (true) {
    
    
                    std::function<void()> task;
                    {
    
    
                        std::unique_lock<std::mutex> lock(queueMutex);
                        condition.wait(lock, [this]() {
    
    
                            return stop || !tasks.empty();
                        });
                        if (stop && tasks.empty()) {
    
    
                            return;
                        }
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    ~ThreadPool() {
    
    
        {
    
    
            std::unique_lock<std::mutex> lock(queueMutex);
            stop = true;
        }
        condition.notify_all();
        for (auto& thread : threads) {
    
    
            thread.join();
        }
    }

    template<typename F, typename... Args>
    void enqueue(F&& f, Args&&... args) {
    
    
        {
    
    
            std::unique_lock<std::mutex> lock(queueMutex);
            tasks.emplace(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
        }
        condition.notify_one();
    }

private:
    std::vector<std::thread> threads;
    std::queue<std::function<void()>> tasks;
    std::mutex queueMutex;
    std::condition_variable condition;
    bool stop;
};

//Ejemplo de grupo de subprocesos

void taskFunction(int id) {
    
    
    std::cout << "Task " << id << " is running" << std::endl;
}

int main() {
    
    
    int numThreads = std::thread::hardware_concurrency();  // 获取可用的线程数

    ThreadPool pool(numThreads);

    // 提交多个任务到线程池
    for (int i = 0; i < 10; ++i) {
    
    
        pool.enqueue(taskFunction, i);
    }

    return 0;
}

En el código anterior, definimos una clase ThreadPool e implementamos un grupo de subprocesos simple utilizando mutexes y variables de condición. Al llamar a la función de puesta en cola, podemos enviar tareas al grupo de subprocesos, y el grupo de subprocesos asignará automáticamente subprocesos para ejecutar tareas. En el ejemplo, creamos un grupo de subprocesos y enviamos 10 tareas al grupo de subprocesos.

Este es solo un ejemplo simple de grupo de subprocesos. En aplicaciones reales, es posible que también deba considerar otros factores, como la prioridad de la tarea, el número máximo de subprocesos en el grupo de subprocesos y el tamaño de la cola de tareas. Para garantizar la seguridad de los subprocesos y una correcta gestión de los recursos, es posible que también deba realizar más mejoras y extensiones.

modelo productor consumidor

El problema productor-consumidor es un problema clásico de programación concurrente que involucra múltiples subprocesos productores y subprocesos consumidores que acceden a un búfer compartido. Los productores colocan datos en buffers y los consumidores sacan datos de buffers. A continuación se muestra un código de muestra que utiliza subprocesos y mutex de C++ para implementar el problema productor-consumidor:

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

const int MAX_QUEUE_SIZE = 10;

std::queue<int> buffer;  // 共享缓冲区
std::mutex mtx;          // 互斥量,用于对缓冲区的访问进行加锁
std::condition_variable cvProducer, cvConsumer;  // 条件变量,用于生产者和消费者之间的同步

void producerFunc(int id) {
    
    
    for (int i = 0; i < 10; ++i) {
    
    
        std::unique_lock<std::mutex> lock(mtx);
        cvProducer.wait(lock, [] {
    
     return buffer.size() < MAX_QUEUE_SIZE; });  // 等待直到缓冲区有空位
        buffer.push(i);
        std::cout << "Producer " << id << " produced: " << i << std::endl;
        lock.unlock();
        cvConsumer.notify_one();  // 通知消费者消费
        std::this_thread::sleep_for(std::chrono::milliseconds(500));  // 模拟生产过程
    }
}

void consumerFunc(int id) {
    
    
    for (int i = 0; i < 10; ++i) {
    
    
        std::unique_lock<std::mutex> lock(mtx);
        cvConsumer.wait(lock, [] {
    
     return !buffer.empty(); });  // 等待直到缓冲区非空
        int data = buffer.front();
        buffer.pop();
        std::cout << "Consumer " << id << " consumed: " << data << std::endl;
        lock.unlock();
        cvProducer.notify_one();  // 通知生产者继续生产
        std::this_thread::sleep_for(std::chrono::milliseconds(500));  // 模拟消费过程
    }
}

int main() {
    
    
    std::thread producerThread1(producerFunc, 1);
    std::thread producerThread2(producerFunc, 2);
    std::thread consumerThread1(consumerFunc, 1);
    std::thread consumerThread2(consumerFunc, 2);

    producerThread1.join();
    producerThread2.join();
    consumerThread1.join();
    consumerThread2.join();

    return 0;
}

En el código anterior, utilizamos std::queue como búfer compartido para almacenar los datos producidos por el productor. std::mutex se usa para bloquear el búfer para garantizar que solo un subproceso pueda acceder al búfer al mismo tiempo. std::condition_variable se utiliza para la sincronización entre productores y consumidores.

El hilo productor usa cvProducer.wait() para esperar la variable de condición después de bloquear el mutex hasta que el búfer esté libre. Una vez que se cumple la condición, el productor coloca los datos en el búfer y luego notifica al hilo del consumidor a través de cvConsumer.notify_one(). Cada vez que el productor produce un dato, dormirá brevemente para simular el proceso de producción.

Después de que el hilo del consumidor también bloquea el mutex, usa cvConsumer.wait() para esperar la variable de condición hasta que el búfer no esté vacío. Una vez que se cumple la condición, el consumidor saca los datos del búfer y notifica al hilo del productor a través de cvProducer.notify_one(). Cada vez que un consumidor consume un dato, también dormirá brevemente para simular el proceso de consumo.

En la función principal, creamos dos subprocesos productores y dos subprocesos consumidores, y llamamos a la función join() respectivamente para esperar el final de los subprocesos.

De esta manera, mediante el uso combinado de mutex y variables de condición, realizamos el procesamiento simultáneo de problemas entre productores y consumidores.

Supongo que te gusta

Origin blog.csdn.net/neuzhangno/article/details/131521091
Recomendado
Clasificación