2012-06-15 19 views
9

Estoy escribiendo un complemento GUI para Node.js (wxWidgets) y quiero ejecutar el bucle GUI en un hilo propio ya que no creo que sea una buena idea fusionar con el hilo principal y el ciclo de eventos de Node.Node.js C++ Addon: Threading

Sin embargo, no estoy seguro de cómo crear un nuevo hilo. Lo tengo funcionando con uv_queue_work(). Pero no creará un hilo exclusivo para la GUI pero usará el grupo de hilos de Node. Y esta podría ser una mala idea, ya que el trabajador se quedará durante todo el tiempo de ejecución. (No estoy seguro de esto)

También podría usar wxWidgets 'wxThread, también funciona. Y encontré una nueva función uv_thread_create en libuv git master. No tengo idea de cómo usar eso ya que no hay descripción y, además, todavía no está disponible en la compilación estable de Node.js.

Mi pregunta: ¿Cuál es la forma "estándar" de crear un complemento Node.js multiproceso, si corresponde? Miré otros proyectos pero solo pude encontrar subprocesos de trabajo de ejecución corta usando libuv.

+0

No sé C++, pero soy consciente de que el proyecto 'nodo-fibers' crea hilos, tal vez sería útil mirar allí (en caso de que se ha perdido): https: // github.com/laverdet/node-fibers/blob/master/src/fibers.cc – alessioalex

+0

También puede consultar https://github.com/xk/node-threads-a-gogo/ y ver cómo implementaron eso. –

Respuesta

9

La respuesta es que normalmente desea utilizar los subprocesos de fondo gestionados por Nodejs mediante el envío de su trabajo a la cola de eventos de ultravioleta, y luego dejar que nodejs se preocupen por cómo crear y gestionar subprocesos.

a continuación es el ejemplo repetitivo que falta en el manual node.js v0.10.

struct Baton 
{ 
    // we need this structure to interact with the uv 
    // the uv_work_t must be the initial element and should store 
    // the callback function to be useful, but the rest 
    // is user defined depending on what is needed to actually do the work. 
    uv_work_t     request; 
    v8::Persistent<v8::Function> callback; 
    // Add more elements to the structure as needed 
    int       countdown; 
}; 


static void AsyncTestWork (uv_work_t* req); 
static void AsyncTestAfter(uv_work_t* req,int status); 
static Handle<Value> AsyncTestPrep(const Arguments& args) 
{ 
    HandleScope scope; 
    if (args.Length() != 1) { 
     ThrowException(Exception::TypeError(String::New("Wrong number of arguments -- needs (callback)"))); 
     return scope.Close(Undefined()); 
    } 

    if (!args[0]->IsFunction()) { 
     ThrowException(Exception::TypeError(String::New("Wrong type of arguments -- needs (callback)"))); 
     return scope.Close(Undefined()); 
    } 

    v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[0]); 

    Baton* baton = new Baton(); 
    baton->request.data = baton; 
    baton->callback = v8::Persistent<v8::Function>::New(callback); 
    baton->countdown = 3; 

    uv_queue_work(uv_default_loop(), &baton->request, AsyncTestWork, AsyncTestAfter); 

    return scope.Close(v8::Undefined()); 
} 


static void AsyncTestWork (uv_work_t* req) 
{ 
    // This method will run in a seperate thread where you can do 
    // your blocking background work. 
    // In this function, you cannot under any circumstances access any V8/node js 
    // valiables -- all data and memory needed, MUSt be in the Baton structure 
    Baton* baton = static_cast<Baton*>(req->data); 
    sleep(6); // some fictional work, delaying the return.... 
    baton->countdown -= 1; // my actual work in this 
} 

static void AsyncTestAfter(uv_work_t* req,int status) 
{ 
    // This is what is called after the 'Work' is done, you can now move any data from 
    // Baton to the V8/Nodejs space and invoke call back functions 

    Baton* baton = static_cast<Baton*>(req->data); 

    v8::Handle<v8::Value> argv1[] = { v8::Null(), Number::New(baton->countdown) }; 
    v8::Handle<v8::Value> argv2[] = { v8::Null(), Number::New(23) }; 

    v8::TryCatch try_catch; 
     // Call back to the JS function, you can make as many/few callbacks 
     // as you need, they just go on the event loop queue for now. 
     // Note: that it is mostly customary to call the callback 
     // function just (exactly) which is probably what you want 
     // to do to avoid confusing your users if you make a public api 
     baton->callback->Call(v8::Context::GetCurrent()->Global(), 2, argv1); 
     baton->callback->Call(v8::Context::GetCurrent()->Global(), 2, argv2); 
    if (try_catch.HasCaught()) { 
     node::FatalException(try_catch); 
    } 

    if (baton->countdown > 0) { 
     // resubmit the worker for yet more work 
     uv_queue_work(uv_default_loop(), &baton->request, AsyncTestWork, AsyncTestAfter); 
    } else { 
     // we are finished, clean up and be done 
     baton->callback.Dispose(); 
     delete baton; 
    } 
} 


void init(Handle<Object> exports) 
{ 

    exports->Set(String::NewSymbol("myAsyncTestFunction"), 
       FunctionTemplate::New(AsyncTestPrep)->GetFunction()); 

} 
+2

Parece que alguien ha terminado esto en una bonita extensión 'npm install nan' para las extensiones de nodej nativas - https://github.com/rvagg/nan – Soren

+0

¡Gracias exactamente lo que estaba buscando! Funciona de maravilla. – webaba