ThreadPool usando ASIO - Salida de subprocesos, tarea no realizada

Estoy escribiendo una clase ThreadPool en C++ usando Boost ASIO. El siguiente es el código que he escrito hasta ahora:

La clase ThreadPool

    using namespace std;
    using namespace boost;

    class ThreadPoolClass {
    private:

        /* The limit to the maximum number of threads to be
         * instantiated within this pool 
         */
        int maxThreads; 
        /* Group of threads in the Pool */
        thread_group threadPool;

        asio::io_service asyncIOService;

        void _Init()
        {
            maxThreads = 0;
        }
    public:
        ThreadPoolClass();
        ThreadPoolClass(int maxNumThreads);
        ThreadPoolClass(const ThreadPoolClass& orig);
        void CreateThreadPool();
        void RunTask(JobClass * aJob);
        virtual ~ThreadPoolClass();

    };
    ThreadPoolClass::ThreadPoolClass() {
    _Init();
    }



ThreadPoolClass::ThreadPoolClass(int maxNumThreads) {
    _Init();
    maxThreads = maxNumThreads;
}

void ThreadPoolClass::CreateThreadPool() {

    asio::io_service::work work(asyncIOService);

    for (int i = 0; i < maxThreads; i++) {
        cout<<"Pushed"<<endl;
        threadPool.create_thread(bind(&asio::io_service::run, &asyncIOService));
    }
}

void ThreadPoolClass::RunTask(JobClass * aJob) {
    cout<<"RunTask"<<endl;
    asyncIOService.post(bind(&JobClass::Run,aJob));
}

ThreadPoolClass::ThreadPoolClass(const ThreadPoolClass& orig) {
}

ThreadPoolClass::~ThreadPoolClass() {
    cout<<"Kill ye all"<<endl;
    asyncIOService.stop();
    threadPool.join_all();
}

la clase de trabajo

using namespace std;

class JobClass {
private:
    int a;
    int b;
    int c;

public:

    JobClass() {
        //Empty Constructor
    }

    JobClass(int val) {
        a = val;
        b = val - 1;
        c = val + 1;
    }

    void Run()
    {
        cout<<"a: "<<a<<endl;
        cout<<"b: "<<b<<endl;
        cout<<"c: "<<c<<endl;
    }

};

Inicio

using namespace std;

int main(int argc, char** argv) {

    ThreadPoolClass ccThrPool(20);
    ccThrPool.CreateThreadPool();
    JobClass ccJob(10);
    cout << "Starting..." << endl;
    while(1)
    {
        ccThrPool.RunTask(&ccJob);
    }
    return 0;
}

Entonces, básicamente estoy creando 20 subprocesos, pero a partir de ahora solo publico una (misma) tarea para que la ejecute ioservice (solo para mantener las cosas simples aquí y llegar a la causa raíz). El siguiente es el resultado cuando ejecuto este programa en GDB:

Pushed
[New Thread 0xb7cd2b40 (LWP 15809)]
Pushed
[New Thread 0xb74d1b40 (LWP 15810)]
Pushed
[New Thread 0xb68ffb40 (LWP 15811)]
Pushed
[New Thread 0xb60feb40 (LWP 15812)]
Pushed
[New Thread 0xb56fdb40 (LWP 15813)]
Pushed
[New Thread 0xb4efcb40 (LWP 15814)]
Pushed
[New Thread 0xb44ffb40 (LWP 15815)]
Pushed
[New Thread 0xb3affb40 (LWP 15816)]
Pushed
[New Thread 0xb30ffb40 (LWP 15817)]
Pushed
[New Thread 0xb28feb40 (LWP 15818)]
Pushed
[New Thread 0xb20fdb40 (LWP 15819)]
Pushed
[New Thread 0xb18fcb40 (LWP 15820)]
Pushed
[New Thread 0xb10fbb40 (LWP 15821)]
Pushed
[New Thread 0xb08fab40 (LWP 15822)]
Pushed
[New Thread 0xb00f9b40 (LWP 15823)]
Pushed
[New Thread 0xaf8f8b40 (LWP 15824)]
Pushed
[New Thread 0xaf0f7b40 (LWP 15825)]
Pushed
[New Thread 0xae8f6b40 (LWP 15826)]
Pushed
[New Thread 0xae0f5b40 (LWP 15827)]
Pushed
[New Thread 0xad8f4b40 (LWP 15828)]
Starting...
RunTask
Kill ye all
[Thread 0xb4efcb40 (LWP 15814) exited]
[Thread 0xb30ffb40 (LWP 15817) exited]
[Thread 0xaf8f8b40 (LWP 15824) exited]
[Thread 0xae8f6b40 (LWP 15826) exited]
[Thread 0xae0f5b40 (LWP 15827) exited]
[Thread 0xaf0f7b40 (LWP 15825) exited]
[Thread 0xb56fdb40 (LWP 15813) exited]
[Thread 0xb18fcb40 (LWP 15820) exited]
[Thread 0xb10fbb40 (LWP 15821) exited]
[Thread 0xb20fdb40 (LWP 15819) exited]
[Thread 0xad8f4b40 (LWP 15828) exited]
[Thread 0xb3affb40 (LWP 15816) exited]
[Thread 0xb7cd2b40 (LWP 15809) exited]
[Thread 0xb60feb40 (LWP 15812) exited]
[Thread 0xb08fab40 (LWP 15822) exited]
[Thread 0xb68ffb40 (LWP 15811) exited]
[Thread 0xb74d1b40 (LWP 15810) exited]
[Thread 0xb28feb40 (LWP 15818) exited]
[Thread 0xb00f9b40 (LWP 15823) exited]
[Thread 0xb44ffb40 (LWP 15815) exited]
[Inferior 1 (process 15808) exited normally]

Tengo dos preguntas:

  1. ¿Por qué mis subprocesos se cierran, incluso cuando estoy publicando tareas en un ciclo while?
  2. ¿Por qué no se imprime la salida de JobClass, es decir, los valores de las variables a, b y c?

preguntado el 08 de septiembre de 12 a las 09:09

2 Respuestas

Creo que esto sucede porque crea un objeto de trabajo en el método CreateThreadPool, que se destruye automáticamente cuando sale del alcance -> en este caso, io_service no tiene trabajo activo y no procesa sus tareas.

Intente hacer una variable de instancia de 'trabajo' de su clase ThreadPool, no una local en el método.

class ThreadPoolClass {
private:

    thread_group threadPool;

    asio::io_service asyncIOService;

    std::auto_ptr<asio::io_service::work> work_;

public:
};



ThreadPoolClass::ThreadPoolClass(int maxNumThreads) {
    _Init();
    maxThreads = maxNumThreads;
}

void ThreadPoolClass::CreateThreadPool() {

    work_.reset(new asio::io_service::work(asyncIOService));

    for (int i = 0; i < maxThreads; i++) {
        cout<<"Pushed"<<endl;
        threadPool.create_thread(bind(&asio::io_service::run, &asyncIOService));
    }
}

Respondido el 10 de Septiembre de 12 a las 14:09

Sugiero usar std::unique_ptr (si lo tiene) en lugar de std::auto_ptr, ya que std::auto_ptr está siendo obsoleto. - Algún tipo programador

@JoachimPileborg: De acuerdo, con los compiladores modernos debería preferir unique_ptr - Nogard

@nogard, tu solución funcionó. ¡Es una tontería de mi parte que no pude ver a través del problema de "variable saliendo del alcance"! Joachim, std:unique_ptr lo es!! - Cuánto cuesta

De acuerdo, seré el primero en admitir que no conozco boost, y más específicamente boost::asio desde un agujero en el suelo, pero sé muchísimo sobre grupos de subprocesos y equipos de trabajo.

Se supone que los subprocesos deben dormir hasta que se les notifique un nuevo trabajo, pero si no están configurados para hacerlo, es probable que simplemente terminen su proceso de subproceso y salgan. Una señal reveladora de que este es el caso es iniciar un grupo, dormir durante un período de tiempo razonable antes de publicar cualquier trabajo, y si todos los subprocesos del grupo están terminando, no están esperando correctamente. Una lectura rápida de los documentos de impulso produjo este y puede estar relacionado con su problema.

En ese sentido, ¿es posible que el destructor de su grupo desde el punto de entrada principal () esté, de hecho, matando prematuramente a su equipo de trabajo? Veo el join_all, pero ese stop() me pone los pelos de punta. si hace lo que su nombre implica, eso explicaría muchas cosas. De acuerdo con la descripción de esa llamada stop() de los documentos:

Para efectuar un cierre, la aplicación deberá llamar a la función miembro stop() del objeto io_service. Esto hará que la llamada io_service run() regrese lo antes posible, abandonando las operaciones inconclusas y sin permitir que se envíen los controladores listos.

Esa mención de cierre inmediato y abandono parece sospechosamente familiar a su situación actual.

Nuevamente, no conozco boost:asio de Adam, pero si estuviera en esto, verificaría la configuración de inicio para los objetos de subproceso de impulso. es probable que requieran configuración sobre cómo comenzar, cómo esperar, etc. Debe haber numerosos ejemplos del uso de boost:asio en la web relacionados con la configuración de lo que está describiendo aquí, es decir, un paradigma de equipo de trabajo. Veo boost::asio a TON en SO, por lo que es probable que también haya muchas preguntas relacionadas o casi relacionadas.

Siéntase libre de degradar esto si no es útil, y pido disculpas si ese es el caso.

Respondido el 08 de Septiembre de 12 a las 10:09

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.